Este tópico descreve como incluir APIs fornecidas pelo framework em testes que avaliam o comportamento de cada fragmento.
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 dos fragmentos, é importante garantir que eles forneçam uma experiência consistente e eficiente em termos de recursos. Observe o seguinte:
- O fragmento não pode depender de uma atividade mãe ou de um fragmento específicos.
- Só crie uma hierarquia de visualização de fragmento se o fragmento estiver visível para o usuário.
Para ajudar a configurar as condições para a realização desses testes, a biblioteca fragment-testing
do AndroidX fornece a
classe FragmentScenario
para criar fragmentos e mudar o
Lifecycle.State
deles.
Declarar dependências
Para usar FragmentScenario
, defina o artefato fragment-testing-manifest
no seu
o arquivo build.gradle
do app usando debugImplementation
e o artefato fragment-testing
usando androidTestImplementation
, como mostrado no
exemplo a seguir:
Groovy
dependencies { def fragment_version = "1.8.5" debugImplementation "androidx.fragment:fragment-testing-manifest:$fragment_version" androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version" }
Kotlin
dependencies { val fragment_version = "1.8.5" debugImplementation("androidx.fragment:fragment-testing-manifest:$fragment_version") androidTestImplementation("androidx.fragment:fragment-testing:$fragment_version") }
Os exemplos de teste nesta página usam declarações das bibliotecas Espresso e Truth (link em inglês). Para mais informações sobre outras bibliotecas de declaração e testes disponíveis, consulte Configurar projetos para o AndroidX Test.
Criar um fragmento
O FragmentScenario
inclui os seguintes métodos para iniciar fragmentos
em testes:
launchInContainer()
, para testar a interface do usuário de um fragmento. OFragmentScenario
anexa o fragmento ao controlador de visualização raiz de uma atividade. Caso contrário, essa atividade de contenção fica vazia.launch()
, para testes sem a interface do usuário do fragmento. OFragmentScenario
anexa esse tipo de fragmento a uma atividade vazia, que não tem uma visualização raiz.
Depois de iniciar um desses tipos de fragmento, o FragmentScenario
orienta o
fragmento em teste para um estado especificado. Por padrão, esse estado é RESUMED
,
mas é possível substituí-lo pelo argumento initialState
. O estado RESUMED
indica que o fragmento está em execução e visível para o usuário. É possível avaliar as informações
sobre os elementos da interface usando os testes de interface do Espresso.
Os exemplos de código a seguir mostram como iniciar o fragmento usando cada método:
Exemplo de launchInContainer()
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
// The "fragmentArgs" argument is optional.
val fragmentArgs = bundleOf(“selectedListItem” to 0)
val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
...
}
}
Exemplo de launch()
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
// The "fragmentArgs" arguments are optional.
val fragmentArgs = bundleOf("numElements" to 0)
val scenario = launchFragment<EventFragment>(fragmentArgs)
...
}
}
Fornecer dependências
Caso seus fragmentos tenham dependências, você pode oferecer versões de teste delas
fornecendo um FragmentFactory
personalizado aos
métodos launchInContainer()
ou launch()
.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val someDependency = TestDependency()
launchFragmentInContainer {
EventFragment(someDependency)
}
...
}
}
Para ver mais informações sobre como usar FragmentFactory
para fornecer
dependências para fragmentos, consulte
Gerenciador de fragmentos.
Orientar o fragmento para um novo estado
Nos testes de interface do seu app, geralmente é suficiente iniciar o fragmento
em teste e começar a testagem de um estado RESUMED
. Em testes de unidade
mais refinados, no entanto, você também pode avaliar o comportamento do fragmento
à medida que ele passa de um estado de ciclo de vida para outro. É possível especificar o
estado inicial passando o argumento initialState
para qualquer uma das
funções launchFragment*()
.
Para orientar o fragmento para um estado de ciclo de vida diferente, chame moveToState()
.
Esse método é compatível com os seguintes estados como argumentos: CREATED
,
STARTED
, RESUMED
e DESTROYED
. Ele simula uma situação
em que o fragmento ou a atividade que contém o fragmento muda o
estado por qualquer motivo.
O exemplo a seguir inicia um fragmento de teste no estado INITIALIZED
e,
em seguida, move-o para o estado RESUMED
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>(
initialState = Lifecycle.State.INITIALIZED
)
// EventFragment has gone through onAttach(), but not onCreate().
// Verify the initial state.
scenario.moveToState(Lifecycle.State.RESUMED)
// EventFragment moves to CREATED -> STARTED -> RESUMED.
...
}
}
Recriar o fragmento
Se o app estiver sendo executado em um dispositivo com poucos recursos, o sistema
poderá destruir a atividade que contém o fragmento. Essa situação
exige que o app recrie o fragmento quando o usuário retorna a ele.
Para simular essa situação, chame recreate()
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
scenario.recreate()
...
}
}
FragmentScenario.recreate()
destrói o fragmento e o host dele e depois os recria. Quando a
classe FragmentScenario
recria o fragmento em teste, ele
retorna ao estado do ciclo de vida em que se encontrava antes de ser destruído.
Como interagir com fragmentos de interface
Para acionar ações de interface no seu fragmento em teste, use os matchers de visualização do Espresso para interagir com elementos na sua visualização:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
onView(withId(R.id.refresh)).perform(click())
// Assert some expected behavior
...
}
}
Se você precisar chamar um método no próprio fragmento, como responder
a uma seleção no menu de opções, faça isso de modo seguro com uma
referência ao fragmento usando
FragmentScenario.onFragment()
e transmitindo uma
FragmentAction
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
scenario.onFragment { fragment ->
fragment.myInstanceMethod()
}
}
}
Testar ações de caixas de diálogo
O FragmentScenario
também é compatível com testes de
fragmentos de caixas de diálogo. Embora os fragmentos de caixas de diálogo
tenham elementos de interface, o layout é preenchido em uma janela separada, e não
na atividade em si. Por isso, use
FragmentScenario.launch()
para testar fragmentos de caixas de diálogo.
O exemplo a seguir testa o processo de dispensa da 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.parentFragmentManager.executePendingTransactions()
assertThat(fragment.dialog).isNull()
}
}
// Assumes that the dialog had a button
// containing the text "Cancel".
onView(withText("Cancel")).check(doesNotExist())
}
}