Pruebas

Introducción y configuración

WorkManager proporciona un artefacto work-testing que ayuda con la prueba de unidades de tus trabajadores para pruebas de instrumentación de Android.

Para usar el artefacto work-testing, deberías agregarlo como dependencia androidTestImplementation en build.gradle. Para obtener más información sobre este tema, consulta la sección Cómo declarar dependencias en las notas de la versión de WorkManager.

Conceptos

work-testing proporciona una implementación especial de WorkManager para el modo de prueba, que se inicializa mediante WorkManagerTestInitHelper.

El artefacto work-testing también proporciona un objeto SynchronousExecutor que facilita la escritura de pruebas de manera síncrona, sin tener que lidiar con varios subprocesos, bloqueos ni seguros.

A continuación, puedes ver un ejemplo de cómo usar todas estas clases juntas.

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

Cómo estructurar pruebas

Ahora que WorkManager se inicializó en modo de prueba, estás listo para probar tus trabajadores.

Supongamos que tienes un EchoWorker que espera inputData y simplemente copia (hace eco) su entrada en 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);
          }
      }
    }

    

Pruebas básicas

A continuación, puedes ver una prueba de instrumentación de Android de EchoWorker. La principal conclusión es que probar EchoWorker en el modo de prueba es muy similar a la manera en que puedes usar EchoWorker en una aplicación real.

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

    

Escribamos otra prueba que garantice que cuando EchoWorker no reciba datos de entrada, Result sea un objeto 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));
    }

    

Cómo simular restricciones, retrasos y trabajo periódico

WorkManagerTestInitHelper te proporciona una instancia de TestDriver, que se puede usar para simular objetos initialDelay, condiciones en las que se cumplen los objetos Constraint para ListenableWorker y también intervalos para PeriodicWorkRequest.

Cómo probar retrasos iniciales

Los objetos Worker pueden tener retrasos iniciales. A fin de probar EchoWorker con un initialDelay, en lugar de tener que esperar el initialDelay en tu prueba, puedes usar el elemento TestDriver para marcar el retraso inicial de los objetos WorkRequest como si existieran.

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

    

Cómo probar restricciones

TestDriver también se puede usar para marcar restricciones con setAllConstraintsMet. A continuación, puedes ver un ejemplo sobre cómo probar un objeto Worker con restricciones.

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

    

Cómo probar el trabajo periódico

TestDriver también expone un objeto setPeriodDelayMet que se puede usar para indicar que un intervalo está completo. A continuación, puedes ver un ejemplo de uso de 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));
    }