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 configurar las condiciones que permiten realizar estas pruebas, AndroidX proporciona una biblioteca, FragmentScenario, para crear fragmentos y cambiar su estado.

Cómo configurar la ubicación del artefacto de pruebas

Para usar FragmentScenario según lo previsto, define el artefacto fragment-testing en el APK de prueba de tu app, como se muestra en el siguiente fragmento de código:

app/build.gradle

    dependencies {
        def fragment_version = "1.0.0"
        // ...
        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 lanzar 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 adjunta el fragmento al controlador de 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 adjunta este tipo de fragmento a una actividad completamente vacía, que no tiene una vista raíz.

Después de iniciar uno de estos tipos de fragmentos, FragmentScenario lleva el fragmento que se está probando al estado RESUMED. Este estado indica que el fragmento se está ejecutando. Si estás probando un fragmento gráfico, también estará visible para los usuarios, por lo que puedes evaluar la información sobre sus elementos de 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; en ese caso, tu app tendrá que volver a crear el fragmento cuando el usuario regrese a ella. Para simular esta situación, llama a recreate() de la siguiente manera:

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

Cuando la clase FragmentScenario recrea el fragmento que se está probando, el fragmento vuelve al estado del ciclo de vida en el que se encontraba antes de ser recreado.

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 llevar al fragmento a un estado de ciclo de vida diferente, 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 tu fragmento cambia su estado porque otra app o una acción del sistema la interrumpe.

En el siguiente fragmento de código, aparece un ejemplo de 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 fragmento, por ejemplo, para responder a una selección en el menú de opciones, puedes hacerlo de manera segura mediante la implementación de 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 pruebas de diálogos. Si bien los diálogos son instancias de fragmentos gráficos, puedes usar el método launchFragment() para que los elementos del diálogo se completen en el diálogo en sí mismo, en lugar de hacerlo en la actividad que inicia el diálogo.

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