As versões v2 das APIs de teste do Compose (createComposeRule,
createAndroidComposeRule, runComposeUiTest,
runAndroidComposeUiTest etc.) já estão disponíveis para melhorar o controle sobre
a execução de corrotinas. Essa atualização não duplica toda a plataforma da API.
Somente as APIs que estabelecem o ambiente de teste foram atualizadas.
As APIs v1 foram descontinuadas, e é altamente recomendável migrar para as novas APIs. A migração verifica se os testes estão alinhados com o comportamento padrão de corrotinas e evita problemas de compatibilidade futuros. Para ver uma lista das APIs v1 descontinuadas, consulte Mapeamentos de API.
Enquanto as APIs v1 dependiam do UnconfinedTestDispatcher, as APIs v2 usam o
StandardTestDispatcher por padrão para a composição em execução. Essa mudança
alinha o comportamento de teste do Compose com as APIs runTest padrão e oferece
controle explícito sobre a ordem de execução de corrotinas.
Configurar o ambiente de teste
As APIs de teste do Compose v2 usam ComposeUiTestConfig para personalizar o ambiente de
teste. As APIs que criam funções de configuração para testes, como
createComposeRule, runComposeUiTest e outras APIs relacionadas,
aceitam ComposeUiTestConfig. Esse objeto de configuração consolida
APIs relacionadas ao ambiente, como effectContext, runTestContext
e testTimeout, em um único objeto.
O modelo de configuração também gerencia inputMode. As APIs de teste do Compose v2
aplicam InputMode.Touch por padrão no início de cada teste para garantir
o determinismo e evitar que o estado do modo de entrada vaze entre os testes.
ComposeUiTestConfig faz parte das APIs de teste do Compose v2, que usam
StandardTestDispatcher por padrão. Se os testes usarem APIs v1,
consulte Migrar para APIs de teste v2 antes de adotar
ComposeUiTestConfig.
Migrar para ComposeUiTestConfig
Nas sobrecargas para criar funções de configuração em testes, várias sobrecargas
que aceitam parâmetros de configuração individuais, como effectContext,
runTestContext ou testTimeout, foram descontinuadas. Atualize seus testes para usar
ComposeUiTestConfig, conforme mostrado no exemplo a seguir:
val testConfig = ComposeUiTestConfig( effectContext = EmptyCoroutineContext, runTestContext = EmptyCoroutineContext, testTimeout = 30.seconds ) @get:Rule val rule = createComposeRule(config = testConfig) // OR runComposeUiTest(config = testConfig) {}
Modo de entrada padrão
Os testes podem falhar durante a migração se dependerem de modos de entrada sem toque
configurados por APIs de instrumentação antes do início do teste. Nas funções de configuração
para testes, o sistema aplica InputMode.Touch por padrão no
início de cada teste para maior determinismo e para evitar vazamento de estado, substituindo
o estado do dispositivo ambiente e a configuração pré-teste.
Para resolver isso, especifique o modo de entrada necessário em ComposeUiTestConfig:
class FocusTest { @get:Rule val rule = createComposeRule( config = ComposeUiTestConfig(inputMode = InputMode.Keyboard) ) @Test fun testFocus() {} }
Para configurar o modo de entrada para casos de teste individuais em vez de toda a
classe de teste, transmita ComposeUiTestConfig para runComposeUiTest:
class FocusTest { @Test fun testTouchMode() = runComposeUiTest { // Runs with the default InputMode.Touch } @Test fun testKeyboardMode() = runComposeUiTest( ComposeUiTestConfig(inputMode = InputMode.Keyboard) ) { // Runs with InputMode.Keyboard } }
Para outros problemas e soluções de migração, consulte Falhas comuns e como corrigi-las.
Migrar para as APIs de teste v2
Ao fazer upgrade para as APIs v2, geralmente é possível usar Localizar + Substituir para atualizar as importações de pacotes e adotar as novas mudanças do agente de distribuição.
Como alternativa, peça ao Gemini para realizar uma migração para a v2 das APIs de teste do Compose com o seguinte comando:
Comando de IA do
Migrar das APIs de teste v1 para as APIs de teste v2
Este comando vai usar este guia para migrar para as APIs de teste v2.
Migrate to Compose testing v2 APIs using the official
migration guide.Use a tabela a seguir para mapear as APIs v1 descontinuadas para as substituições da v2:
Descontinuado (v1) |
Substituição (v2) |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Compatibilidade com versões anteriores e exceções
As APIs v1 atuais foram descontinuadas, mas continuam usando
UnconfinedTestDispatcher para manter o comportamento atual e evitar alterações
interruptivas.
A única exceção em que o comportamento padrão mudou é a seguinte:
O dispatcher de teste padrão usado para executar a composição na classe
AndroidComposeUiTestEnvironment mudou de
UnconfinedTestDispatcher para StandardTestDispatcher. Isso afeta os casos em que
você cria uma instância usando o construtor ou a subclasse
AndroidComposeUiTestEnvironment e chama esse construtor.
Mudança principal: impacto na execução de corrotinas
A principal diferença entre as versões 1 e 2 das APIs é como as corrotinas são enviadas:
- APIs v1 (
UnconfinedTestDispatcher): quando uma corrotina era iniciada, ela era executada imediatamente na linha de execução atual, geralmente terminando antes da próxima linha de código de teste. Ao contrário do comportamento de produção, essa execução imediata pode mascarar inadvertidamente problemas reais de tempo ou disputas que ocorreriam em um aplicativo ativo. - APIs v2 (
StandardTestDispatcher): quando uma corrotina é iniciada, ela é colocada em fila e não é executada até que o teste avance explicitamente o relógio virtual. As APIs de teste padrão do Compose (comowaitForIdle()) já processam essa sincronização. Portanto, a maioria dos testes que dependem dessas APIs padrão vai continuar funcionando sem mudanças.
Falhas comuns e como corrigi-las
Se os testes falharem após o upgrade para a v2, é provável que eles apresentem o seguinte padrão:
- Falha: você inicia uma tarefa (por exemplo, um ViewModel carrega dados), mas a declaração falha imediatamente porque os dados ainda estão em um estado "Carregando".
- Causa: com as APIs v2, as corrotinas são colocadas em fila em vez de serem executadas imediatamente. A tarefa foi colocada na fila, mas nunca foi executada antes de o resultado ser verificado.
- Correção: avance o tempo explicitamente. É preciso informar explicitamente ao dispatcher v2 quando executar o trabalho.
Abordagem anterior
Na v1, a tarefa era iniciada e concluída imediatamente. Na v2, o código a seguir
falha porque loadData() ainda não foi executado.
// In v1, this launched and finished immediately.
viewModel.loadData()
// In v2, this fails because loadData() hasn't actually run yet!
assertEquals(Success, viewModel.state.value)
Abordagem recomendada
Use waitForIdle ou runOnIdle para executar tarefas em fila antes de
fazer uma declaração.
Opção 1: o uso de waitForIdle avança o relógio até que a IU fique inativa,
verificando se a corrotina foi executada.
viewModel.loadData()
// Explicitly run all queued tasks
composeTestRule.waitForIdle()
assertEquals(Success, viewModel.state.value)
Opção 2: o uso de runOnIdle executa o bloco de código na linha de execução de interface depois
que ela fica inativa.
viewModel.loadData()
// Run the assertion after the UI is idle
composeTestRule.runOnIdle {
assertEquals(Success, viewModel.state.value)
}
Sincronização manual
Em cenários que envolvem sincronização manual, como quando o avanço automático está
desativado, iniciar uma corrotina não resulta em execução imediata porque
o relógio de teste está pausado. Para executar corrotinas na fila sem
avançar o relógio virtual, use a API runCurrent(). Isso executa tarefas
programadas para o horário virtual atual.
composeTestRule.mainClock.scheduler.runCurrent()
Ao contrário de waitForIdle(), que avança o relógio de teste até que a IU
se estabilize, runCurrent() executa tarefas pendentes enquanto mantém o tempo virtual
atual. Esse comportamento permite a verificação de estados intermediários que,
de outra forma, seriam ignorados se o relógio fosse avançado para um estado inativo.
O programador de teste usado no ambiente de teste é exposto. Esse
programador pode ser usado em conjunto com a API runTest do Kotlin para
sincronizar o relógio de teste.
Migrar para runComposeUiTest
Se você estiver usando as APIs de teste do Compose com a API runTest do Kotlin,
é altamente recomendável mudar para runComposeUiTest.
Abordagem anterior
O uso de createComposeRule em conjunto com runTest cria dois relógios
separados: um para o Compose e outro para o escopo da corrotina de teste. Essa
configuração pode forçar você a sincronizar manualmente o programador de testes.
@get:Rule val composeTestRule = createComposeRule() @Test fun testWithCoroutines() { composeTestRule.setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000) status = "Done!" } Text(text = status) } // NOT RECOMMENDED // Fails: runTest creates a new, separate scheduler. // Advancing time here does NOT advance the compose clock. // To fix this without migrating, you would need to share the scheduler // by passing 'composeTestRule.mainClock.scheduler' to runTest. runTest { composeTestRule.onNodeWithText("Loading...").assertIsDisplayed() advanceTimeBy(1000) composeTestRule.onNodeWithText("Done!").assertIsDisplayed() } }
Abordagem recomendada
A API runComposeUiTest executa automaticamente o bloco de teste no próprio escopo
runTest. O relógio de teste é sincronizado com o ambiente do Compose, então
não é mais necessário gerenciar o programador manualmente.
@Test fun testWithCoroutines() = runComposeUiTest { setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000) status = "Done!" } Text(text = status) } onNodeWithText("Loading...").assertIsDisplayed() mainClock.advanceTimeBy(1000 + 16 /* Frame buffer */) onNodeWithText("Done!").assertIsDisplayed() } }