Teste os fragmentos do seu app

Os fragmentos servem como contêineres reutilizáveis no seu app, permitindo que o mesmo layout da interface do usuário seja apresentado em diversas atividades e configurações de layout. Considerando a versatilidade desses fragmentos, é importante garantir que eles forneçam uma experiência consistente e eficiente em termos de recursos:

  • A aparência do seu fragmento precisa ser consistente nas configurações de layout, incluindo configurações compatíveis com tamanhos de tela maiores ou com a orientação do dispositivo em paisagem.
  • Não crie uma hierarquia de exibição de um fragmento, a menos que o fragmento seja visível para o usuário.

Este documento descreve como incluir APIs fornecidas pelo framework nos testes que avaliam o comportamento de cada fragmento.

Orientar o estado de um fragmento

Para ajudar a configurar as condições para a realização desses testes, o AndroidX fornece uma biblioteca, FragmentScenario, para criar fragmentos e alterar seu estado.

Configurar o local do artefato de teste

Para usar o FragmentScenario da maneira pretendida, defina o artefato de teste de fragmento no APK de teste do seu app, conforme mostrado no snippet de código a seguir:

app/build.gradle

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

Para visualizar as versões atuais dessa biblioteca, consulte as informações de fragmento na página de versões.

Criar um fragmento

FragmentScenario inclui métodos para iniciar os seguintes tipos de fragmentos:

Os métodos também são compatíveis com os tipos de fragmentos a seguir:

  • Fragmentos gráficos, que contêm uma interface de usuário. Para iniciar esse tipo de fragmento, chame launchFragmentInContainer(). FragmentScenario anexa o fragmento ao controlador de visualização raiz de uma atividade. Caso contrário, essa atividade contida ficará vazia.
  • Fragmentos não gráficos (às vezes chamados de headless fragment), que armazenam ou executam processamentos de curto prazo sobre as informações incluídas em várias atividades. Para iniciar esse tipo de fragmento, chame launchFragment(). FragmentScenario anexa esse tipo de fragmento a uma atividade totalmente vazia, que não possui uma visualização raiz.

Depois de iniciar um desses tipos de fragmento, FragmentScenario orienta o fragmento em teste para o estado RESUMED. Esse estado indica que o fragmento está em execução. Se você estiver testando um fragmento gráfico, ele também estará visível para os usuários. Portanto, é possível avaliar as informações sobre seus elementos de IU usando os testes de IU do Espresso.

Os snippets de código a seguir mostram como iniciar cada tipo de fragmento:

Exemplo 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!")))
        }
    }
    

Exemplo de fragmento não 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)
        }
    }
    

Recriar o fragmento

Caso um dispositivo esteja com poucos recursos, o sistema poderá destruir a atividade que contém seu fragmento, exigindo que seu app recrie o fragmento quando o usuário retornar ao app. Para simular essa situação, chame recreate():

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

Quando a classe FragmentScenario recria o fragmento em teste, o fragmento retorna ao estado do ciclo de vida em que se encontrava antes de ser recriado.

Orientar o fragmento para um novo estado

Geralmente, apenas iniciar e recriar o fragmento em teste nos testes de IU do app é suficiente. Contudo, em testes de unidade mais refinados, também é possível avaliar o comportamento do fragmento à medida que ele passa de um estado de ciclo de vida para outro.

Para orientar o fragmento para um estado de ciclo de vida diferente, chame moveToState(). Esses métodos são compatíveis com os seguintes estados como argumentos: CREATED, STARTED, RESUMED e DESTROYED. Essa ação simula uma situação em que a atividade que contém seu fragmento altera seu estado porque é interrompida por outro app ou por uma ação do sistema.

Um exemplo de uso de moveToState() aparece no snippet de código a seguir:

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

Acionar ações no fragmento

Para acionar ações no seu fragmento em teste, use os correspondentes de visualização do Espresso para interagir com elementos na sua exibição:

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

Caso seja necessário chamar um método no próprio fragmento, como responder a uma seleção no menu de opções, será possível fazê-lo com segurança 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.
                }
            }
        }
    }
    

Testar ações de caixas de diálogo

FragmentScenario também é compatível com testes de caixas de diálogo. Embora as caixas de diálogo sejam instâncias de fragmentos gráficos, o método launchFragment() é usado para que os elementos da caixa de diálogo sejam preenchidos nela mesma e não na atividade que a inicia.

O snippet de código a seguir testa o processo de dispensar a caixa de 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())
            }
        }
    }