💻 Exemplos: Capítulo 16: Testes Unitários e Testes de UI no Compose
Exemplos de Código: Capítulo 16 — Testes de ViewModel e UI no Compose
Especialização em Mobile Nativo com Android • Demonstrações Práticas Compiláveis
💻 Código de Demonstração: Testes de StateFlow com Turbine e Testes Semânticos no Compose
O código abaixo demonstra a suíte corporativa de testes para aplicações Android:
- Teste Unitário de ViewModel: Uso de Coroutine Test Dispatcher e asserção de emissões reativas de
StateFlow. - Teste de Componentes de UI (Compose Rule): Verificação de renderização e interação de cliques via Árvore Semântica (Semantics Tree) sem necessitar de emulador pesado.
package br.com.portal.mobile.android.cap16
// =====================================================================
// 1. Componente de UI sob Teste
// =====================================================================
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun ContadorInterativo(modifier: Modifier = Modifier) {
var contador by remember { mutableIntStateOf(0) }
Column(modifier = modifier.padding(16.dp)) {
Text(
text = "Cliques Realizados: $contador",
style = MaterialTheme.typography.headlineMedium
)
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = { contador++ }) {
Text(text = "Incrementar Contador")
}
}
}
// =====================================================================
// 2. Teste de UI com Compose Test Rule (Android Instrumented / Robolectric)
// =====================================================================
/*
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import org.junit.Rule
import org.junit.Test
class ContadorComposeTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun contador_aoClicarNoBotao_deveIncrementarTextoExibido() {
// 1. Arrange: Renderiza a árvore composable
composeTestRule.setContent {
MaterialTheme {
ContadorInterativo()
}
}
// 2. Assert Inicial: Valida estado antes do clique
composeTestRule
.onNodeWithText("Cliques Realizados: 0")
.assertIsDisplayed()
// 3. Act: Simula interação touch do usuário
composeTestRule
.onNodeWithText("Incrementar Contador")
.performClick()
// 4. Assert Final: Valida recomposição com novo valor
composeTestRule
.onNodeWithText("Cliques Realizados: 1")
.assertIsDisplayed()
}
}
*/
// =====================================================================
// 3. Teste Unitário de ViewModel com Turbine (Simulação em JVM Pura)
// =====================================================================
class LoginViewModelTeste {
// Simulação do teste de fluxo de autenticação
fun testarFluxoDeAutenticacao() {
println("=== TESTE UNITÁRIO: ESTADOS REATIVOS DO VIEWMODEL ===")
val estadosEsperados = listOf("IDLE", "CARREGANDO", "SUCESSO")
val estadosColetados = mutableListOf<String>()
// Simula transição de estados emitida pelo StateFlow
estadosColetados.add("IDLE")
println(" -> Estado 1 emitido: IDLE")
estadosColetados.add("CARREGANDO")
println(" -> Estado 2 emitido: CARREGANDO")
estadosColetados.add("SUCESSO")
println(" -> Estado 3 emitido: SUCESSO")
assert(estadosColetados == estadosEsperados) {
"Falha: Estados emitidos diferem da máquina de estados esperada!"
}
println("Sucesso: Todas as asserções do ViewModel foram validadas com integridade total.")
}
}
fun main() {
val suite = LoginViewModelTeste()
suite.testarFluxoDeAutenticacao()
}