Cómo compilar pruebas de unidades locales

Puedes evaluar la lógica de tu app con pruebas de unidades locales si necesitas ejecutar pruebas con mayor rapidez y no precisas la fidelidad y la confianza asociadas con la ejecución de pruebas en un dispositivo real. Con este enfoque, cumples con las relaciones de dependencia utilizando Robolectric o un marco de trabajo ficticio, como Mockito. Por lo general, los tipos de dependencias asociados con las pruebas determinan qué herramienta usar:

  • Si tienes dependencias en el framework de Android, en especial, aquellas que crean interacciones complejas, se recomienda incluir dependencias del framework con Robolectric.
  • Si las pruebas tienen dependencias mínimas del framework de Android, o si dependen solo de sus propios objetos, es correcto incluir dependencias ficticias con un framework ficticio, como Mockito.

Cómo configurar el entorno de prueba

En tu proyecto de Android Studio, debes almacenar los archivos de origen para las pruebas de unidades locales en module-name/src/test/java/. Ese directorio ya existe cuando creas un proyecto nuevo.

También debes configurar las dependencias de prueba para que tu proyecto use las API estándar proporcionadas por el marco de trabajo de JUnit 4. Si la prueba debe interactuar con dependencias de Android, incluye Robolectric o la biblioteca de Mockito para simplificar las pruebas de unidades locales.

En el archivo build.gradle de nivel superior de la app, especifica las siguientes bibliotecas como dependencias:

    dependencies {
        // Required -- JUnit 4 framework
        testImplementation 'junit:junit:4.12'
        // Optional -- Robolectric environment
        testImplementation 'androidx.test:core:1.0.0'
        // Optional -- Mockito framework
        testImplementation 'org.mockito:mockito-core:1.10.19'
    }
    

Cómo crear una clase de prueba de unidades local

La clase de prueba de unidades local se debe escribir como una clase de prueba JUnit 4. JUnit es el marco de trabajo de prueba de unidades más popular y más usado para Java, y te permite escribir las pruebas de una manera más simple y flexible que las versiones anteriores, ya que con JUnit 4 no es necesario que hagas lo siguiente:

  • Extender la clase junit.framework.TestCase
  • Prefijar el nombre del método de prueba con la palabra clave 'test'
  • Usar clases de los paquetes junit.framework o junit.extensions

Para crear una clase de prueba básica de JUnit 4, crea una clase que contenga uno o más métodos de prueba. Un método de prueba comienza con la anotación @Test y contiene el código para ejecutar y verificar una funcionalidad única en el componente que quieres probar.

En el siguiente ejemplo, se muestra cómo implementar una clase de prueba de unidad local. El método de prueba emailValidator_CorrectEmailSimple_ReturnsTrue verifica que el método isValidEmail() de la app bajo evaluación muestre el resultado correcto.

Kotlin

    import com.google.common.truth.Truth.assertThat
    import org.junit.Test

    class EmailValidatorTest {
        @Test
        fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
            assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue()
        }
    }
    

Java

    import com.google.common.truth.Truth.assertThat;
    import org.junit.Test;

    public class EmailValidatorTest {
        @Test
        public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
            assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue();
        }
    }
    

Para crear pruebas legibles que evalúen si los componentes de la app muestran los resultados esperados, recomendamos usar la biblioteca de Truth y las clases de aserciones de Android, como se muestra en el ejemplo anterior. Para obtener más información sobre los tipos de validación lógica que admiten las afirmaciones de Android y Truth, consulta la sección que describe cómo crear aserciones más legibles.

Sin embargo, si te sientes más cómodo cuando comparas los resultados esperados y los reales con los métodos junit.Assert o los comparadores de Hamcrest (como los métodos is() y equalTo()), está bien usar esas bibliotecas.

Nota: Hamcrest sigue siendo la biblioteca preferida para usar en la creación de comparadores, como la clase ViewMatcher de Espresso.

Cómo incluir dependencias en el framework

Si tus pruebas interactúan con varias dependencias del marco de trabajo de Android o con esas dependencias de manera compleja, usa los artefactos de Robolectric que proporciona AndroidX Test. Robolectric ejecuta el código del marco de trabajo de Android real y simulaciones del código del marco de trabajo nativo en la JVM local o en un dispositivo real.

En el siguiente ejemplo, se muestra cómo puedes crear una prueba de unidad que use Robolectric:

app/build.gradle

    android {
        // ...
        testOptions {
            unitTests.includeAndroidResources = true
        }
    }
    

MyLocalUnitTestClass

Kotlin

    import android.content.Context
    import androidx.test.core.app.ApplicationProvider
    import com.google.common.truth.Truth.assertThat
    import org.junit.Test

    private const val FAKE_STRING = "HELLO_WORLD"

    class UnitTestSample {
        val context = ApplicationProvider.getApplicationContext<Context>()

        @Test fun readStringFromContext_LocalizedString() {
            // Given a Context object retrieved from Robolectric...
            val myObjectUnderTest = ClassUnderTest(context)

            // ...when the string is returned from the object under test...
            val result: String = myObjectUnderTest.getHelloWorldString()

            // ...then the result should be the expected one.
            assertThat(result).isEqualTo(FAKE_STRING)
        }
    }
    

Java

    import android.content.Context;
    import androidx.test.core.app.ApplicationProvider;
    import org.junit.Test;

    import static com.google.common.truth.Truth.assertThat;

    public class UnitTestSampleJava {
        private static final String FAKE_STRING = "HELLO_WORLD";
        private Context context = ApplicationProvider.getApplicationContext();

        @Test
        public void readStringFromContext_LocalizedString() {
            // Given a Context object retrieved from Robolectric...
            ClassUnderTest myObjectUnderTest = new ClassUnderTest(context);

            // ...when the string is returned from the object under test...
            String result = myObjectUnderTest.getHelloWorldString();

            // ...then the result should be the expected one.
            assertThat(result).isEqualTo(FAKE_STRING);
        }
    }
    

Cómo incluir clases de Android Builder

Si creas pruebas de unidades locales que ejecutas en un entorno de Robolectric o en un dispositivo real, puedes usar los creadores que proporciona AndroidX Test para varias clases de marcos de trabajo comunes. Esos creadores te permiten generar instancias de las siguientes clases sin necesidad de usar ubicaciones ficticias ni reflejos:

Cómo usar la clase de utilidad Parcelables

Además, la biblioteca proporciona una clase de utilidad para los objetos Parcelable. Cuando se proporciona un objeto Creator, la clase deserializa el objeto Parcelable dado y, luego, serializa un objeto Parcelable duplicado.

Nota: Corresponde al método que llama a Parcelables.forceParcel() verificar que la operación de deserializar/reserializar haya sido exitosa.

Cómo incluir dependencias ficticias

De forma predeterminada, el complemento de Android para Gradle ejecuta las pruebas de unidades locales en una versión modificada de la biblioteca android.jar, que no contiene ningún código real. En cambio, las llamadas de método a las clases de Android desde la prueba de unidades arrojan una excepción. Esto es para asegurarte de que probarás solo el código y que no dependerás de ningún comportamiento particular de la plataforma de Android (que no has compilado o para el que no has generado ubicaciones ficticias explícitamente).

Cómo generar dependencias ficticias de Android

Si tienes dependencias mínimas de Android y necesitas probar interacciones específicas entre un componente y su dependencia dentro de la app, usa un framework ficticio para eliminar las dependencias externas del código. De esta manera, puedes probar fácilmente que el componente interactúa con la dependencia de la manera esperada. Al sustituir las dependencias de Android con objetos ficticios, puedes aislar la prueba de unidad del resto del sistema Android mientras verificas que se llame a los métodos correctos en esas dependencias. El marco de trabajo ficticio de Mockito para Java (versión 1.9.5 y posteriores) ofrece compatibilidad con las pruebas de unidades de Android. Con Mockito, puedes configurar objetos ficticios para mostrar algún valor específico cuando se invoca.

Para agregar un objeto ficticio a la prueba de unidad local con este framework, sigue el modelo de programación:

  1. Incluye la dependencia de la biblioteca de Mockito en el archivo build.gradle, según lo indicado en Cómo configurar tu entorno de prueba.
  2. Al comienzo de la definición de la clase de prueba de unidades, agrega la anotación @RunWith(MockitoJUnitRunner.class). Ese objeto le indica al panel de pruebas de Mockito que valide que tu uso del framework es correcto y simplifica la inicialización de los objetos ficticios.
  3. A fin de crear un objeto ficticio para una dependencia de Android, agrega la anotación @Mock antes de la declaración del campo.
  4. Para indicar el comportamiento de la dependencia, puedes especificar una condición y el valor mostrado cuando se cumpla la condición mediante los métodos when() y thenReturn().

En el siguiente ejemplo, se muestra cómo puedes crear una prueba de unidad que utilice un objeto Context ficticio.

Kotlin

    import android.content.Context
    import com.google.common.truth.Truth.assertThat
    import org.junit.Test
    import org.junit.runner.RunWith
    import org.mockito.Mock
    import org.mockito.Mockito.`when`
    import org.mockito.junit.MockitoJUnitRunner

    private const val FAKE_STRING = "HELLO WORLD"

    @RunWith(MockitoJUnitRunner::class)
    class UnitTestSample {

        @Mock
        private lateinit var mockContext: Context

        @Test
        fun readStringFromContext_LocalizedString() {
            // Given a mocked Context injected into the object under test...
            `when`(mockContext.getString(R.string.hello_word))
                    .thenReturn(FAKE_STRING)
            val myObjectUnderTest = ClassUnderTest(mockContext)

            // ...when the string is returned from the object under test...
            val result: String = myObjectUnderTest.getHelloWorldString()

            // ...then the result should be the expected one.
            assertThat(result, `is`(FAKE_STRING))
        }
    }
    

Java

    import android.content.Context;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.Mock;
    import org.mockito.junit.MockitoJUnitRunner;

    import static com.google.common.truth.Truth.assertThat;
    import static org.mockito.Mockito.when;

    @RunWith(MockitoJUnitRunner.class)
    public class UnitTestSample {

        private static final String FAKE_STRING = "HELLO WORLD";

        @Mock
        Context mockContext;

        @Test
        public void readStringFromContext_LocalizedString() {
            // Given a mocked Context injected into the object under test...
            when(mockContext.getString(R.string.hello_world))
                    .thenReturn(FAKE_STRING);
            ClassUnderTest myObjectUnderTest = new ClassUnderTest(mockContext);

            // ...when the string is returned from the object under test...
            String result = myObjectUnderTest.getHelloWorldString();

            // ...then the result should be the expected one.
            assertThat(result, is(FAKE_STRING));
        }
    }
    

Para obtener más información sobre el uso del framework de Mockito, consulta la referencia de la API de Mockito y la clase SharedPreferencesHelperTest en el código de muestra. Prueba también Android Testing Codelab.

Error: "Método… no ficticio"

Si ejecutas una prueba que llama a una API desde el SDK de Android que no es ficticia, se mostrará un error que indica que el método no es ficticio. Se debe a que el archivo android.jar utilizado para ejecutar pruebas de unidades no contiene ningún código real (esas API solo las proporciona la imagen del sistema de Android en un dispositivo).

En cambio, todos los métodos muestran excepciones de forma predeterminada. La finalidad es asegurarte de que las pruebas de unidades solo prueben tu código y no dependan de ningún comportamiento particular de la plataforma de Android (para el que no has generado una ubicación ficticia explícitamente, por ejemplo, con Mockito).

Si las excepciones generadas son problemáticas para las pruebas, puedes cambiar el comportamiento a fin de que, en su lugar, los métodos muestren un valor nulo o cero mediante la siguiente configuración en el archivo build.gradle de nivel superior del proyecto:

    android {
      ...
      testOptions {
        unitTests.returnDefaultValues = true
      }
    }
    

Precaución: Debes tener cuidado cuando configuras la propiedad returnDefaultValues como true. Los valores nulo/cero que se muestran pueden introducir regresiones en las pruebas, que son difíciles de depurar y pueden permitir que se aprueben las pruebas fallidas. Úsalo solo como último recurso.

Cómo ejecutar pruebas de unidades locales

Para ejecutar pruebas de unidades locales, sigue estos pasos:

  1. Asegúrate de que tu proyecto esté sincronizado con Gradle. Para ello, haz clic en la opción Sync Project de la barra de herramientas.
  2. Ejecuta tu prueba de una de las siguientes maneras:
    • Para ejecutar una sola prueba, abre la ventana Project, haz clic con el botón derecho en una prueba y, luego, haz clic en Run .
    • Para probar todos los métodos de una clase, haz clic con el botón derecho en una clase o método en el archivo de prueba y luego haz clic en Run .
    • Para ejecutar todas las pruebas de un directorio, haz clic con el botón derecho en el directorio y selecciona Run tests .

El complemento de Android para Gradle compila el código de prueba de unidades locales ubicado en el directorio predeterminado (src/test/java/), compila una app de prueba y la ejecuta localmente con la clase predeterminada de panel de prueba. Android Studio luego muestra los resultados en la ventana Run.