Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

テストする

導入とセットアップ

WorkManager は、Android インストルメンテーション テスト用のワーカーのユニットテストに役立つ work-testing アーティファクトを備えています。

work-testing アーティファクトを使用するには、build.gradle 内の androidTestImplementation 依存関係として追加する必要があります。詳細については、WorkManager リリースノートの「依存関係を宣言する」をご覧ください。

概念

work-testing は、WorkManagerTestInitHelper を使用して初期化されるテストモード用 WorkManager の特別な実装を実現します。

また、work-testing アーティファクトは、SynchronousExecutor も備えており、複数のスレッド、ロック、ラッチを処理することなく、同期方式で簡単にテストを作成することができます。

上記のクラスをすべて一緒に使用する方法の例を以下に示します。

Kotlin

    @RunWith(AndroidJUnit4::class)
    class BasicInstrumentationTest {
        @Before
        fun setup() {
            val context = InstrumentationRegistry.getTargetContext()
            val config = Configuration.Builder()
                // Set log level to Log.DEBUG to make it easier to debug
                .setMinimumLoggingLevel(Log.DEBUG)
                // Use a SynchronousExecutor here to make it easier to write tests
                .setExecutor(SynchronousExecutor())
                .build()

            // Initialize WorkManager for instrumentation tests.
            WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
        }
    }
    

Java

    @RunWith(AndroidJUnit4.class)
    public class BasicInstrumentationTest {
        @Before
        public void setup() {
            Context context = InstrumentationRegistry.getTargetContext();
            Configuration config = new Configuration.Builder()
                    // Set log level to Log.DEBUG to
                    // make it easier to see why tests failed
                    .setMinimumLoggingLevel(Log.DEBUG)
                    // Use a SynchronousExecutor to make it easier to write tests
                    .setExecutor(new SynchronousExecutor())
                    .build();

            // Initialize WorkManager for instrumentation tests.
            WorkManagerTestInitHelper.initializeTestWorkManager(
                context, config);
        }
    }
    

テストを構造化する

WorkManager がテストモードで初期化され、ワーカーをテストする準備が整いました。

たとえば、EchoWorker だとします。このワーカーは、inputData を想定し、その入力を outputData にシンプルにコピー(エコー)します。

Kotlin

    class EchoWorker(context: Context, parameters: WorkerParameters)
       : Worker(context, parameters) {
       override fun doWork(): Result {
           return when(inputData.size()) {
               0 -> Result.failure()
               else -> Result.success(inputData)
           }
       }
    }

    

Java

    public class EchoWorker extends Worker {
      public EchoWorker(Context context, WorkerParameters parameters) {
          super(context, parameters);
      }

      @NonNull
      @Override
      public Result doWork() {
          Data input = getInputData();
          if (input.size() == 0) {
              return Result.failure();
          } else {
              return Result.success(input);
          }
      }
    }

    

基本的なテスト

EchoWorker をテストする Android インストルメンテーション テストを以下に示します。重要なポイントとして、テストモードで EchoWorker をテストする方法は、実際のアプリで EchoWorker を使用する方法と似ています。

Kotlin

    @Test
    @Throws(Exception::class)
    fun testSimpleEchoWorker() {
        // Define input data
        val input = workDataOf(KEY_1 to 1, KEY_2 to 2)

        // Create request
        val request = OneTimeWorkRequestBuilder<EchoWorker>()
            .setInputData(input)
            .build()

        val workManager = WorkManager.getInstance(applicationContext)
        // Enqueue and wait for result. This also runs the Worker synchronously
        // because we are using a SynchronousExecutor.
        workManager.enqueue(request).result.get()
        // Get WorkInfo and outputData
        val workInfo = workManager.getWorkInfoById(request.id).get()
        val outputData = workInfo.outputData
        // Assert
        assertThat(workInfo.state, `is`(WorkInfo.State.SUCCEEDED))
        assertThat(outputData, `is`(input))
    }

    

Java

    @Test
    public void testSimpleEchoWorker() throws Exception {
       // Define input data
       Data input = new Data.Builder()
               .put(KEY_1, 1)
               .put(KEY_2, 2)
               .build();

       // Create request
       OneTimeWorkRequest request =
           new OneTimeWorkRequest.Builder(EchoWorker.class)
               .setInputData(input)
               .build();

       WorkManager workManager = WorkManager.getInstance(getApplicationContext());
       // Enqueue and wait for result. This also runs the Worker synchronously
       // because we are using a SynchronousExecutor.
       workManager.enqueue(request).getResult().get();
       // Get WorkInfo and outputData
       WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
       Data outputData = workInfo.getOutputData();
       // Assert
       assertThat(workInfo.getState(), is(WorkInfo.State.SUCCEEDED));
       assertThat(outputData, is(input));
    }

    

EchoWorker が入力データを取得しなかったときに Result が想定どおり Result.failure() になるか確認する別のテストを作成しましょう。

Kotlin

    @Test
    @Throws(Exception::class)
    fun testEchoWorkerNoInput() {
       // Create request
       val request = OneTimeWorkRequestBuilder<EchoWorker>()
           .build()

       val workManager = WorkManager.getInstance(applicationContext)
       // Enqueue and wait for result. This also runs the Worker synchronously
       // because we are using a SynchronousExecutor.
       workManager.enqueue(request).result.get()
       // Get WorkInfo
       val workInfo = workManager.getWorkInfoById(request.id).get()
       // Assert
       assertThat(workInfo.state, `is`(WorkInfo.State.FAILED))
    }

    

Java

    @Test
    public void testEchoWorkerNoInput() throws Exception {
      // Create request
      OneTimeWorkRequest request =
          new OneTimeWorkRequest.Builder(EchoWorker.class)
             .build();

      WorkManager workManager = WorkManager.getInstance(getApplicationContext());
      // Enqueue and wait for result. This also runs the Worker synchronously
      // because we are using a SynchronousExecutor.
      workManager.enqueue(request).getResult().get();
      // Get WorkInfo
      WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
      // Assert
      assertThat(workInfo.getState(), is(WorkInfo.State.FAILED));
    }

    

制約、遅延、周期的処理をシミュレーションする

WorkManagerTestInitHelper を使用すると、initialDelay をシミュレーションする際に使用できる TestDriver のインスタンスや、ListenableWorker に対して Constraint が満たされる条件、PeriodicWorkRequest の間隔を指定できます。

初期遅延をテストする

Worker は、初期遅延が発生することがあります。initialDelay のある EchoWorker をテストするには、テスト内で initialDelay を待つのではなく、TestDriver を使用して、WorkRequest 初期遅延が満たされた場合にマーキングします。

Kotlin

    @Test
    @Throws(Exception::class)
    fun testWithInitialDelay() {
        // Define input data
        val input = workDataOf(KEY_1 to 1, KEY_2 to 2)

        // Create request
        val request = OneTimeWorkRequestBuilder<EchoWorker>()
            .setInputData(input)
            .setInitialDelay(10, TimeUnit.SECONDS)
            .build()

        val workManager = WorkManager.getInstance(getApplicationContext())
        val testDriver = WorkManagerTestInitHelper.getTestDriver()
        // Enqueue and wait for result.
        workManager.enqueue(request).result.get()
        testDriver.setInitialDelayMet(request.id)
        // Get WorkInfo and outputData
        val workInfo = workManager.getWorkInfoById(request.id).get()
        val outputData = workInfo.outputData
        // Assert
        assertThat(workInfo.state, `is`(WorkInfo.State.SUCCEEDED))
        assertThat(outputData, `is`(input))
    }

    

Java

    @Test
    public void testWithInitialDelay() throws Exception {
      // Define input data
      Data input = new Data.Builder()
              .put(KEY_1, 1)
              .put(KEY_2, 2)
              .build();

      // Create request
      OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(EchoWorker.class)
              .setInputData(input)
              .setInitialDelay(10, TimeUnit.SECONDS)
              .build();

      WorkManager workManager = WorkManager.getInstance(myContext);
      // Get the TestDriver
      TestDriver testDriver = WorkManagerTestInitHelper.getTestDriver();
      // Enqueue
      workManager.enqueue(request).getResult().get();
      // Tells the WorkManager test framework that initial delays are now met.
      testDriver.setInitialDelayMet(request.getId());
      // Get WorkInfo and outputData
      WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
      Data outputData = workInfo.getOutputData();
      // Assert
      assertThat(workInfo.getState(), is(WorkInfo.State.SUCCEEDED));
      assertThat(outputData, is(input));
    }

    

制約をテストする

TestDriver を使用すると、制約が満たされた場合に setAllConstraintsMet を使用してマーキングすることもできます。制約付きの Worker をテストする方法の例を以下に示します。

Kotlin

    @Test
    @Throws(Exception::class)
    fun testWithConstraints() {
        // Define input data
        val input = workDataOf(KEY_1 to 1, KEY_2 to 2)

        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()

        // Create request
        val request = OneTimeWorkRequestBuilder<EchoWorker>()
            .setInputData(input)
            .setConstraints(constraints)
            .build()

        val workManager = WorkManager.getInstance(myContext)
        val testDriver = WorkManagerTestInitHelper.getTestDriver()
        // Enqueue and wait for result.
        workManager.enqueue(request).result.get()
        testDriver.setAllConstraintsMet(request.id)
        // Get WorkInfo and outputData
        val workInfo = workManager.getWorkInfoById(request.id).get()
        val outputData = workInfo.outputData
        // Assert
        assertThat(workInfo.state, `is`(WorkInfo.State.SUCCEEDED))
        assertThat(outputData, `is`(input))
    }

    

Java

    @Test
    public void testWithConstraints() throws Exception {
        // Define input data
        Data input = new Data.Builder()
                .put(KEY_1, 1)
                .put(KEY_2, 2)
                .build();

        // Define constraints
        Constraints constraints = new Constraints.Builder()
                .setRequiresDeviceIdle(true)
                .build();

        // Create request
        OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(EchoWorker.class)
                .setInputData(input)
                .setConstraints(constraints)
                .build();

        WorkManager workManager = WorkManager.getInstance(myContext);
        TestDriver testDriver = WorkManagerTestInitHelper.getTestDriver();
        // Enqueue
        workManager.enqueue(request).getResult().get();
        // Tells the testing framework that all constraints are met.
        testDriver.setAllConstraintsMet(request.getId());
        // Get WorkInfo and outputData
        WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
        Data outputData = workInfo.getOutputData();
        // Assert
        assertThat(workInfo.getState(), is(WorkInfo.State.SUCCEEDED));
        assertThat(outputData, is(input));
    }

    

周期的処理をテストする

TestDriver は、間隔が完了したことを示すことができる setPeriodDelayMet もエクスポーズします。setPeriodDelayMet の使用例を以下に示します。

Kotlin

    @Test
    @Throws(Exception::class)
    fun testPeriodicWork() {
        // Define input data
        val input = workDataOf(KEY_1 to 1, KEY_2 to 2)

        // Create request
        val request = PeriodicWorkRequestBuilder<EchoWorker>(15, MINUTES)
            .setInputData(input)
            .build()

        val workManager = WorkManager.getInstance(myContext)
        val testDriver = WorkManagerTestInitHelper.getTestDriver()
        // Enqueue and wait for result.
        workManager.enqueue(request).result.get()
        // Tells the testing framework the period delay is met
        testDriver.setPeriodDelayMet(request.id)
        // Get WorkInfo and outputData
        val workInfo = workManager.getWorkInfoById(request.id).get()
        // Assert
        assertThat(workInfo.state, `is`(WorkInfo.State.ENQUEUED))
    }

    

Java

    @Test
    public void testPeriodicWork() throws Exception {
        // Define input data
        Data input = new Data.Builder()
                .put(KEY_1, 1)
                .put(KEY_2, 2)
                .build();

        // Create request
        PeriodicWorkRequest request =
                new PeriodicWorkRequest.Builder(EchoWorker.class, 15, MINUTES)
                .setInputData(input)
                .build();

        WorkManager workManager = WorkManager.getInstance(myContext);
        TestDriver testDriver = WorkManagerTestInitHelper.getTestDriver();
        // Enqueue
        workManager.enqueue(request).getResult().get();
        // Tells the testing framework the period delay is met
        testDriver.setPeriodDelayMet(request.getId());
        // Get WorkInfo and outputData
        WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
        // Assert
        assertThat(workInfo.getState(), is(WorkInfo.State.ENQUEUED));
    }