Cómo probar los fragmentos de tu app

Los fragmentos sirven como contenedores reutilizables dentro de tu app, lo que te permite presentar el mismo diseño de interfaz de usuario en una variedad de actividades y configuraciones de diseño. Dada la versatilidad de estos fragmentos, es importante validar que proporcionen una experiencia constante y eficiente en el uso de recursos:

  • La apariencia de tu fragmento debe ser constante en todas las configuraciones de diseño, incluidas las que admiten pantallas de mayor tamaño o la orientación de dispositivo horizontal.
  • No crees la jerarquía de vista de un fragmento a menos que el fragmento sea visible para el usuario.

En este documento, se describe cómo incluir las API proporcionadas por el marco de trabajo en las pruebas que evalúan el comportamiento de cada fragmento.

Cómo controlar el estado de un fragmento

A fin de ayudar a establecer las condiciones para realizar estas pruebas, AndroidX proporciona una biblioteca, FragmentScenario, que permite crear fragmentos y cambiar su estado.

Cómo declarar dependencias

Para usar FragmentScenario según su destino previsto, define el artefacto de prueba de fragmentos en el APK de prueba de la app, tal como se muestra en el siguiente fragmento de código:

app/build.gradle

    dependencies {
        def fragment_version = "1.2.4"
        // ...
        debugImplementation 'androidx.fragment:fragment-testing:$fragment_version'
    }
    

Para ver las versiones actuales de esta biblioteca, consulta la información sobre Multidex en la página de versiones.

Cómo crear un fragmento

FragmentScenario incluye métodos para iniciar los siguientes tipos de fragmentos:

Los métodos también admiten los siguientes tipos de fragmentos:

  • Fragmentos gráficos, que contienen una interfaz de usuario. Para iniciar este tipo de fragmento, llama a launchFragmentInContainer(). FragmentScenario conecta el fragmento al controlador de la vista raíz de una actividad. Esta actividad que lo contiene está vacía de otra manera.
  • Fragmentos no gráficos (a veces denominados fragmentos sin interfaz gráfica) que almacenan o realizan un procesamiento a corto plazo de información incluida en varias actividades. Para iniciar este tipo de fragmento, llama a launchFragment(). FragmentScenario conecta este tipo de fragmento a una actividad completamente vacía que no tiene una vista de raíz.

Después de iniciar uno de estos tipos de fragmentos, FragmentScenario cambia el fragmento sometido a prueba al estado RESUMED. Este estado indica que el fragmento se está ejecutando. Si pruebas un fragmento gráfico, también estará visible para los usuarios, para que puedes evaluar la información sobre los elementos de la IU con las pruebas de IU Espresso.

En los siguientes fragmentos de código, se muestra cómo iniciar cada tipo de fragmento:

Ejemplo de fragmento gráfico

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEventFragment() {
            // The "fragmentArgs" and "factory" arguments are optional.
            val fragmentArgs = Bundle().apply {
                putInt("selectedListItem", 0)
            }
            val factory = MyFragmentFactory()
            val scenario = launchFragmentInContainer<MyFragment>(
                    fragmentArgs, factory)
            onView(withId(R.id.text)).check(matches(withText("Hello World!")))
        }
    }
    

Ejemplo de fragmento no gráfico

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEventFragment() {
            // The "fragmentArgs" and "factory" arguments are optional.
            val fragmentArgs = Bundle().apply {
                putInt("numElements", 0)
            }
            val factory = MyFragmentFactory()
            val scenario = launchFragment<MyFragment>(fragmentArgs, factory)
        }
    }
    

Cómo recrear el fragmento

Si un dispositivo tiene pocos recursos, es posible que el sistema destruya la actividad que contiene tu fragmento. Como consecuencia la app deberá recrear el fragmento cuando el usuario regrese a la app. Para simular esta situación, llama a recreate():

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEventFragment() {
            val scenario = launchFragmentInContainer<MyFragment>()
            scenario.recreate()
        }
    }
    

Cuando la clase FragmentScenario recrea el fragmento sometido a prueba, este regresa al estado de ciclo de vida en el que se encontraba antes de recrearse.

Cómo llevar al fragmento a un nuevo estado

En las pruebas de IU de tu app, por lo general, basta con iniciar y volver a crear el fragmento que se está probando. Sin embargo, en pruebas de unidades más detalladas, también puedes evaluar el comportamiento del fragmento mientras pasa de un estado de ciclo de vida a otro.

Para cambiar el estado de ciclo de vida de un fragmento, llama a moveToState(). Este método admite los siguientes estados como argumentos: CREATED, STARTED, RESUMED y DESTROYED. Esta acción simula una situación en la que la actividad que contiene el fragmento cambia de estado porque otra ap o una acción del sistema la interrumpe.

En el siguiente fragmento de código, se muestra un ejemplo del uso de moveToState():

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEventFragment() {
            val scenario = launchFragmentInContainer<MyFragment>()
            scenario.moveToState(State.CREATED)
        }
    }
    

Cómo activar acciones en el fragmento

A fin de activar acciones en tu fragmento que se está probando, usa los comparadores de vistas de Espresso para interactuar con los elementos en tu vista:

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEventFragment() {
            val scenario = launchFragmentInContainer<MyFragment>()
            onView(withId(R.id.refresh))
                    .perform(click())
        }
    }
    

Si necesitas llamar a un método en el propio fragmento, como responder a una selección en el menú de opciones, puedes hacerlo de forma segura implementando FragmentAction:

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testEventFragment() {
            val scenario = launchFragmentInContainer<MyFragment>()
            scenario.onFragment(fragment ->
                fragment.onOptionsItemSelected(clickedItem) {
                    // Update fragment's state based on selected item.
                }
            }
        }
    }
    

Acciones del diálogo de prueba

FragmentScenario también admite la prueba de diálogos. Aunque los diálogos son instancias de fragmentos gráficos, el método launchFragment() se utiliza para que los elementos del diálogo se propaguen en el diálogo, en lugar de hacerlo en la actividad que lo inicia.

En el siguiente fragmento de código, se prueba el proceso de descarte del diálogo:

    @RunWith(AndroidJUnit4::class)
    class MyTestSuite {
        @Test fun testDismissDialogFragment() {
            // Assumes that "MyDialogFragment" extends the DialogFragment class.
            with(launchFragment<MyDialogFragment>()) {
                onFragment { fragment ->
                    assertThat(fragment.dialog).isNotNull()
                    assertThat(fragment.requireDialog().isShowing).isTrue()
                    fragment.dismiss()
                    fragment.requireFragmentManager().executePendingTransactions()
                    assertThat(fragment.dialog).isNull()
                }

                // Assumes that the dialog had a button
                // containing the text "Cancel".
                onView(withText("Cancel")).check(doesNotExist())
            }
        }
    }