Um recurso inativo representa uma operação assíncrona cujos resultados afetam operações subsequentes em um teste de interface. Ao registrar recursos de inatividade no Espresso, você pode validar essas operações assíncronas com mais confiabilidade ao testar seu app.
Identificar quando os recursos de inatividade são necessários
O Espresso oferece um conjunto sofisticado de
recursos de sincronização. Essa
característica do framework, no entanto, se aplica apenas a operações que publicam
mensagens na MessageQueue
, como uma subclasse de
View
que está desenhando o conteúdo na tela.
Como o Espresso não reconhece nenhuma outra operação assíncrona, incluindo aquelas em execução em uma linha de execução em segundo plano, ele não pode oferecer garantias de sincronização nessas situações. Para que o Espresso reconheça as operações de longa duração do app, é necessário registrar cada uma como recurso de inatividade.
Se você não usa recursos de inatividade ao testar os resultados do trabalho assíncrono do app, pode ser necessário usar uma das seguintes soluções alternativas inadequadas para melhorar a confiabilidade dos testes:
- Adicionar chamadas a
Thread.sleep()
. Quando você adiciona atrasos artificiais aos testes, o conjunto de testes leva mais tempo para terminar a execução, e seus testes ainda podem falhar algumas vezes quando executados em dispositivos mais lentos. Além disso, esses atrasos não são bem dimensionados, já que seu app pode precisar executar um trabalho assíncrono mais demorado em uma versão futura. - Implementar wrappers de repetição,que usam uma repetição para verificar repetidamente se o app ainda está executando um trabalho assíncrono até que o tempo limite seja atingido. Mesmo que você especifique uma contagem máxima de tentativas nos testes, cada nova execução consome recursos do sistema, especialmente a CPU.
- Uso de instâncias de
CountDownLatch
,que permitem que uma ou mais linhas de execução aguardem até que um número específico de operações executadas em outra linha de execução seja concluído. Esses objetos exigem que você especifique um tempo limite. Caso contrário, o app poderá ser bloqueado indefinidamente. As travas também adicionam complexidade desnecessária ao código, dificultando a manutenção.
O Espresso permite remover essas soluções alternativas não confiáveis dos testes e registrar o trabalho assíncrono do app como recursos de inatividade.
Casos de uso comuns
Ao executar operações semelhantes aos exemplos a seguir nos testes, considere usar um recurso de inatividade:
- Carregar dados da Internet ou de uma fonte de dados local.
- Estabelecer conexões com bancos de dados e callbacks.
- Gerenciar serviços usando um serviço do sistema ou uma instância de
IntentService
. - Executar lógica de negócios complexa, como transformações de bitmap.
É especialmente importante registrar recursos de inatividade quando essas operações atualizam uma interface que é validada pelos testes.
Exemplo de implementação de recursos de inatividade
A lista a seguir descreve vários exemplos de implementações de recursos de inatividade que você pode integrar ao seu app:
CountingIdlingResource
- Mantém um contador de tarefas ativas. Quando o contador é zero, o recurso associado é considerado inativo. Essa funcionalidade se parece muito com a de um
Semaphore
. Na maioria dos casos, essa implementação é suficiente para gerenciar o trabalho assíncrono do app durante o teste. UriIdlingResource
- Semelhante a
CountingIdlingResource
, mas o contador precisa ser zero por um período específico antes que o recurso seja considerado inativo. Esse período de espera considera solicitações de rede consecutivas, em que um app na linha de execução pode fazer uma nova solicitação imediatamente depois de receber uma resposta a uma solicitação anterior. IdlingThreadPoolExecutor
- Uma implementação personalizada de
ThreadPoolExecutor
que monitora o número total de tarefas em execução nos pools de linhas de execução criados. Essa classe usa umCountingIdlingResource
para manter o contador de tarefas ativas .
IdlingScheduledThreadPoolExecutor
- Uma implementação personalizada de
ScheduledThreadPoolExecutor
. Ela fornece as mesmas funcionalidades e recursos da classeIdlingThreadPoolExecutor
, mas também pode monitorar tarefas programadas para o futuro ou para execução periódica.
Criar o próprio recurso de inatividade
Ao usar recursos de inatividade nos testes do app, pode ser necessário fornecer gerenciamento ou geração de registros de recursos personalizados. Nesses casos, as implementações listadas na seção anterior podem não ser suficientes. Se esse for o caso, estenda uma dessas implementações de recurso de inatividade ou crie uma própria.
Se você implementar sua própria funcionalidade de recurso de inatividade, lembre-se das seguintes práticas recomendadas, especialmente a primeira:
- Invocar transições para o estado inativo fora das verificações de inatividade
- Depois que o app ficar inativo, chame
onTransitionToIdle()
fora das implementações deisIdleNow()
. Dessa forma, o Espresso não fará uma segunda verificação desnecessária para determinar se um determinado recurso inativo está inativo.
O snippet de código a seguir ilustra essa recomendação:
Kotlin
fun isIdle() { // DON'T call callback.onTransitionToIdle() here! } fun backgroundWorkDone() { // Background work finished. callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle. // Don't do any post-processing work beyond this point. Espresso now // considers your app to be idle and moves on to the next test action. }
Java
public void isIdle() { // DON'T call callback.onTransitionToIdle() here! } public void backgroundWorkDone() { // Background work finished. callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle. // Don't do any post-processing work beyond this point. Espresso now // considers your app to be idle and moves on to the next test action. }
- Registrar recursos de inatividade antes que eles se tornem necessários
Os benefícios de sincronização associados aos recursos de inatividade só entram em vigor depois que o Espresso invoca o método
isIdleNow()
desse recurso pela primeira vez.A lista a seguir mostra vários exemplos dessa propriedade:
- Se você registrar um recurso de inatividade em um método anotado com
@Before
, esse recurso entrará em vigor na primeira linha de cada teste. - Se você registrar um recurso de inatividade em um teste, esse recurso entrará em vigor durante a próxima ação baseada no Espresso. Esse comportamento ainda ocorrerá mesmo que a próxima ação esteja no mesmo teste da instrução que registra o recurso de inatividade.
- Se você registrar um recurso de inatividade em um método anotado com
- Cancelar o registro de recursos de inatividade depois de usá-los
Para economizar recursos do sistema, cancele o registro de recursos de inatividade assim que não precisar mais deles. Por exemplo, se você registrar um recurso de inatividade em um método anotado com
@Before
, é melhor cancelar o registro desse recurso em um método correspondente anotado com@After
.- Usar um registro de inatividade para registrar e cancelar o registro de recursos de inatividade
Ao usar esse contêiner para os recursos de inatividade do seu app, é possível registrar e cancelar o registro desses recursos repetidamente, conforme necessário, e ainda observar um comportamento consistente.
- Manter apenas o estado simples do app nos recursos de inatividade
Por exemplo, os recursos de inatividade que você implementa e registra não podem conter referências a objetos
View
.
Registrar recursos de inatividade
O Espresso oferece uma classe de contêiner em que é possível colocar os recursos de inatividade
do app. Essa classe, chamada
IdlingRegistry
, é um
artefato autônomo que introduz uma sobrecarga mínima no app. Ela
também permite seguir estas etapas para melhorar a
capacidade de manutenção do app:
- Nos testes do app, crie uma referência ao
IdlingRegistry
, em vez dos recursos de inatividade que ele contém. - Mantenha diferenças na coleção de recursos de inatividade que você usa para cada variante de build.
- Defina recursos de inatividade nos serviços do app, em vez de nos componentes da IU que se referem a esses serviços.
Integrar recursos de inatividade ao app
Embora você possa adicionar recursos de inatividade a um app de várias maneiras diferentes, uma abordagem em particular mantém o encapsulamento do app e, ao mesmo tempo, permite que você especifique uma operação específica representada por um determinado recurso de inatividade.
Abordagem recomendada
Ao adicionar recursos de inatividade ao app, é altamente recomendável colocar a lógica de recursos inativos no próprio app e realizar apenas as operações de registro e cancelamento de registro nos seus testes.
Embora você crie a situação incomum de usar uma interface somente teste no código de produção seguindo essa abordagem, é possível envolver recursos de inatividade em torno do código que você já tem, mantendo o tamanho do APK e a contagem de métodos do seu app.
Abordagens alternativas
Se você preferir não ter a lógica de recursos de inatividade no código de produção do app, há várias outras estratégias de integração viáveis:
- Crie variantes de build, como as variações de produto do Gradle, e use recursos de inatividade somente no build de depuração do app.
- Use um framework de injeção de dependência como o Dagger (em inglês) para injetar o gráfico de dependência de recursos de inatividade do app nos seus testes. Se você está usando o Dagger 2, a injeção em si precisa se originar de um subcomponente.
Implemente um recurso de inatividade nos testes do app e exponha a parte da implementação que precisa ser sincronizada nesses testes.
Cuidado : embora essa decisão de design pareça criar uma referência independente a recursos de inatividade, ela também quebra o encapsulamento em todos os apps, exceto nos mais simples.
Outros recursos
Para saber mais sobre o uso do Espresso em testes do Android, consulte os recursos abaixo.
Exemplos
- IdlingResourceSample: sincronização com jobs em segundo plano.