daybreaksnow's diary

私を私と呼びたい

[JUnit4]Ruleアノテーションを使って共通する処理をまとめる

複数のテストケースで共通の初期化処理があるとき、以下のようにリソースクラスを作って、各テストクラスでセットアップメソッドを呼んでいた

・共通するリソースクラス

public class DatabaseConnectionResource{
 private SessionFactory sessionFactory;
 
 public void setUp(){
  sessionFactory = createSessionFactory();
 }

 public void tearDown(){
  sessionFactory.close();
 }
}

・テストクラス

public class HogeTest{
 private static DatabaseConnectionResource connection;
 
 @BeforeClass
 public static void setUp(){
  connection = new DatabaseConnectionResource();
  connection.setUp();
 }

 @AfterClass
 public statis void tearDown(){
  connection.tearDown();
 }
}


これをJUnit4のRuleアノテーションを使うともっと簡潔に書けるようになる。
・共通するリソースクラス

public class DatabaseConnectionResource extends ExternalResource{
  private SessionFactory sessionFactory;

  @Override
  protected void before() {
   sessionFactory = createSessionFactory();
  }

 @Override
 protected void after() {
  sessionFactory.close();
 }
}

・テストクラス

public class HogeTest{
 @ClassRule
 public static DatabaseConnectionResource connection = new DatabaseConnectionResource();
 
}

ClassRuleアノテーションを付けると、@BeforeClass、@AfterClassのタイミングでそのオブジェクトの対応メソッドが呼ばれる。
また、ClassRuleを付けたフィールドは可視性がpublicでなければならない。(そうでないと実行時に怒られる)

@Before、@Afterに対応する場合は@ClassRuleではなく、Ruleアノテーションが利用できる。

また、複数のRuleがある場合はその実行順序は定義されないが、RuleChainを使ってインスタンスを生成することで、指定した順序で呼ばれるようになる。

//outer、innerの順でbeforeメソッドが呼ばれ、その逆順でafterメソッドが呼ばれる
@Rule
public RuleChain ruleChain = RuleChain.outerRule(new ExternalResourceOuter()).around(new ExternalResourceInner());

なお、@ClassRuleはJUnit4.9から、RuleChainはJUnit4.10から追加された機能となる。