欢迎参加我们将于 6 月 3 日举行的 #Android11:Beta 版发布会

测试

简介与设置

WorkManager 提供了一个 work-testing 工件,可以协助进行 Android 插桩测试中的工作器单元测试。

要使用 work-testing 工件,您应该将它作为 androidTestImplementation 依赖项添加到 build.gradle 中。如需有关此主题的详细信息,请参阅 WorkManager 版本说明中的“声明依赖项”部分。

概念

work-testing 为测试模式提供了一种特殊的 WorkManager 实现,它通过使用 WorkManagerTestInitHelper 来初始化。

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 没有获得任何输入数据时,其预期的 ResultResult.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 为您提供了一个 TestDriver 实例,可用于模拟 initialDelayListenableWorker 满足 Constraint 的条件,以及 PeriodicWorkRequest 的间隔。

测试初始延迟

Worker 可以具有初始延迟。要测试含有 EchoWorkerinitialDelay,而不必在测试中等待 initialDelay,您可以使用 TestDriverWorkRequest 初始延迟标记为已满足条件。

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));
    }