新たなテストルールの作り方を紹介します。
JUnitとAndroidJUnitにはいくつかの有用なテストルールがすでに用意されています。しかし、十分とは言えません。テストルールを自作してテスト環境を機能拡張しましょう!
テストルールの役割
テストルールはテストに必要なボイラープレートコードを削減します。
ボイラープレートとは「省く事ができない定型的な処理」のことです。テストを記述すると、各テストにボイラープレートコードが頻繁に出てきます。
例えばActivityがありA、B、Cの3つの機能を持つとします。この3つの機能をテストする時、各々のテストには準備でActivityを起動し、終わりでActivityを停止する処理が必要になります。Activityの機能をテストするのですから、Activityの起動と停止が必要なのは当たり前ですよね。
テストクラスにActivityの起動と停止を記述すると次のようになります。この記述がボイラープレートコードです。
@RunWith(AndroidJUnit4::class)
class SampleATest {
lateinit var activity: Activity
@Before
fun setUp() {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val context = instrumentation.targetContext
val intent = Intent(context, MainActivity::class.java)
activity = instrumentation.startActivitySync(intent)
instrumentation.waitForIdleSync()
}
@After
fun tearDown() {
activity.finish()
}
@Test
fun 機能Aのユースケース() {
// 機能Aのユースケースをチェック
}
}
@RunWith(AndroidJUnit4.class)
public class SampleATest {
private Activity mActivity;
@Before
public void setUp() {
Instrumentation _Instrumentation
= InstrumentationRegistry.getInstrumentation();
Context _Context = _Instrumentation.getTargetContext();
Intent _Intent = new Intent(_Context, MainActivity.class);
mActivity = _Instrumentation.startActivitySync(_Intent);
_Instrumentation.waitForIdleSync();
}
@After
public void tearDown() {
mActivity.finish();
}
@Test
public void 機能Aのユースケース() {
// 機能Aのユースケースをチェック
}
}
AndroidJunitにルールActivityTestRuleがあります。これは例に挙げたActivityの起動と停止を行うテストルールです。ルールActivityTestRuleを使うと次のようになります。
@RunWith(AndroidJUnit4::class)
class SampleA_Test {
@get:Rule
val activityTestRule = ActivityTestRule(MainActivity::class.java)
@Before
fun setUp() {
}
@After
fun tearDown() {
}
@Test
fun 機能Aのユースケース() {
// 機能Aのユースケースをチェック
}
}
@RunWith(AndroidJUnit4.class)
public class SampleA_Test {
@Rule
public ActivityTestRule<MainActivity> mActivityRule
= new ActivityTestRule<>(MainActivity.class);
@Before
public void setUp() {
}
@After
public void tearDown() {
}
@Test
public void 機能Aのユースケース() {
// 機能Aのユースケースをチェック
}
}
ボイラープレートコードがなくなり、シンプルになりました。
※実際のルールActivityTestRuleは内部でもっと複雑な処理をしています。
テストルールの作成
テストクラスにボイラープレートコードが出てきたら、新たなテストルールを作ってみましょう。テストルールに置き換えると記述もすっきりするし、作ったテストルールは後々のテストで再利用できるし、一石二鳥ですよ。
作成方法は簡単です。TestRuleを継承したクラスを作り、テストの実行(Statement#evaluate)を前処理(before)、後処理(after)で挟むだけです。
class CustomRule : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
try {
before() // 前処理
base.evaluate() // テストの実施
} finally {
after() // 後処理
}
}
}
}
private fun before() {
ここで前処理を行う
}
private fun after() {
ここで後処理を行う
}
}
public class CustomRule implements TestRule {
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
before(); // 前処理
base.evaluate(); // テストの実施
}
finally {
after(); // 後処理
}
}
};
}
private void before() {
ここで前処理を行う
}
private void after() {
ここで後処理を行う
}
}
注意点はtry{…}finally{…}を使い、後処理はfinally{…}に記述することです。これはテストがfailになったとき、後処理が必ず実行されることを保証するためです。
テストルールの処理の順番は?
複数のテストルールをテストクラスに記述したとき、どういう順番で実行されるのか、気になったので調べてみました。
@RunWith(AndroidJUnit4::class)
class CustomRule_Test {
@get:Rule
val ruleA = CustomRule_A() // (3)
@get:Rule
val ruleB = CustomRule_B() // (2)
@get:Rule
val ruleC = CustomRule_C() // (1)
@Before
fun setUp() {
Log.i("CustomRule_Test", "Execute setUp")
}
@After
fun tearDown() {
Log.i("CustomRule_Test", "Execute tearDown")
}
@Test
fun カスタムルールの順番をテスト() {
Log.i("CustomRule_Test", "テストの実施")
}
}
@RunWith(AndroidJUnit4.class)
public class CustomRule_Test {
@Rule
public CustomRule_A mRuleA = new CustomRule_A(); // (3)
@Rule
public CustomRule_B mRuleB = new CustomRule_B(); // (2)
@Rule
public CustomRule_C mRuleC = new CustomRule_C(); // (1)
@Before
public void setUp() {
Log.i("CustomRule_Test", "Execute setUp");
}
@After
public void tearDown() {
Log.i("CustomRule_Test", "Execute tearDown");
}
@Test
public void カスタムルールの順番をテスト() {
Log.i("CustomRule_Test", "テストの実施");
}
}
I/CustomRule_C: Execute before I/CustomRule_B: Execute before I/CustomRule_A: Execute before I/CustomRule_Test: Execute setUp I/CustomRule_Test: テストの実施 I/CustomRule_Test: Execute tearDown I/CustomRule_A: Execute after I/CustomRule_B: Execute after I/CustomRule_C: Execute after
順番はC⇒B⇒A⇒テスト⇒A⇒B⇒Cとなります。
ただ、実行順に依存関係のあるルールは、原因が解明しづらいfailを生むことになりそうなので避けるべきだと思います。
