Testar as atividades do app

As atividades servem como contêineres para cada interação do usuário no seu app. Por isso, é importante testar como as atividades do app se comportam durante eventos no dispositivo, como:

  • Outro app, como o de telefone, interrompe a atividade.
  • O sistema destrói e recria sua atividade.
  • O usuário coloca sua atividade em um novo ambiente de janelas, como picture-in-picture (PIP) ou de várias janelas.

Especificamente, é importante garantir que a atividade se comporte corretamente em resposta aos eventos descritos em Ciclo de vida da atividade.

Este guia descreve como avaliar a capacidade do app de manter a integridade dos dados e uma boa experiência do usuário à medida que as atividades fazem a transição por diferentes estados do ciclo de vida.

Direcionar o estado de uma atividade

Um aspecto fundamental dos testes de atividades do app envolve colocá-las em estados específicos. Para definir essa parte "determinada" dos testes, use instâncias de ActivityScenario, parte da biblioteca AndroidX Test. Com essa classe, você pode colocar a atividade em estados que simulam eventos no nível do dispositivo.

A ActivityScenario é uma API multiplataforma que pode ser usada em testes de unidade locais e de integração no dispositivo. Em um dispositivo real ou virtual, ActivityScenario fornece segurança de linha de execução, sincronizando eventos entre a linha de execução de instrumentação do teste e a linha que executa sua atividade em teste.

A API é particularmente adequada para avaliar como uma atividade em teste se comporta quando é destruída ou criada. Esta seção apresenta os casos de uso mais comuns associados a essa API.

Criar uma atividade

Para criar a atividade em teste, adicione o código mostrado no snippet a seguir:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
       launchActivity<MyActivity>().use {
       }
    }
}

Depois de criar a atividade, o ActivityScenario faz a transição dela para o estado RESUMED. Esse estado indica que a atividade está em execução e está visível para os usuários. Nesse estado, você pode interagir com os elementos View da atividade usando os testes de interface do Espresso.

O Google recomenda que você chame close na atividade quando o teste for concluído. Isso limpa os recursos associados e melhora a estabilidade dos testes. O ActivityScenario implementa o Closeable, para que você possa aplicar a extensão use ou try-with-resources na linguagem de programação Java, para que a atividade seja fechada automaticamente.

Como alternativa, é possível usar ActivityScenarioRule para chamar ActivityScenario.launch automaticamente antes de cada teste e ActivityScenario.close na desmontagem do teste. O exemplo a seguir mostra como definir uma regra e receber uma instância de um cenário dela:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>()

    @Test fun testEvent() {
        val scenario = activityScenarioRule.scenario
    }
}

Direcionar a atividade para um novo estado

Para direcionar a atividade para um estado diferente, como CREATED ou STARTED, chame moveToState(). Essa ação simula uma situação em que a atividade é interrompida ou pausada, respectivamente, 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 testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.moveToState(State.CREATED)
        }
    }
}

Determinar o estado atual da atividade

Para determinar o estado atual de uma atividade em teste, acesse o valor do campo state no objeto ActivityScenario. É particularmente útil verificar o estado de uma atividade em teste se ela redirecionar para outra atividade ou se for concluída, conforme demonstrado no snippet de código abaixo:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.onActivity { activity ->
              startActivity(Intent(activity, MyOtherActivity::class.java))
            }

            val originalActivityState = scenario.state
        }
    }
}

Recriar a atividade

Quando um dispositivo está com poucos recursos, o sistema pode destruir uma atividade, exigindo que o app a recrie quando o usuário retornar. Para simular essas condições, chame recreate():

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.recreate()
        }
    }
}

A classe ActivityScenario mantém o estado da instância salva da atividade e todos os objetos anotados usando @NonConfigurationInstance. Esses objetos são carregados na nova instância da atividade em teste.

Recuperar resultados de atividades

Para receber o código de resultado ou os dados associados a uma atividade concluída, acesse o valor do campo result no objeto ActivityScenario, conforme mostrado no snippet de código abaixo:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testResult() {
        launchActivity<MyActivity>().use {
            onView(withId(R.id.finish_button)).perform(click())

            // Activity under test is now finished.

            val resultCode = scenario.result.resultCode
            val resultData = scenario.result.resultData
        }
    }
}

Acionar ações na atividade

Todos os métodos em ActivityScenario são chamadas de bloqueio. Portanto, a API exige que você os execute na linha de execução de instrumentação.

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

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use {
            onView(withId(R.id.refresh)).perform(click())
        }
    }
}

No entanto, se você precisar chamar um método na própria atividade, poderá fazer isso com segurança implementando ActivityAction:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.onActivity { activity ->
              activity.handleSwipeToRefresh()
            }
        }
    }
}