@testing-library/react-native (RNTL) testa componentes da maneira como os usuários interagem com eles - por meio de texto visível, roles de acessibilidade e labels - não por detalhes de implementação como estado interno ou métodos privados.
RNTL renderiza componentes com Test Renderer - uma árvore do lado do JS que espelha os componentes host do React Native (View, Text, Pressable).
As consultas percorrem a árvore renderizada procurando por nós que correspondam à acessibilidade e ao texto - os mesmos sinais que a tecnologia assistiva e os usuários confiam.
screen é um namespace vinculado à chamada render() mais recente - reconsulte após atualizações de estado em vez de armazenar em cache referências de elementos de antes das re-renderizações.
userEvent agenda interações como um usuário real (pressionar, digitar, rolar) e retorna promessas - fireEvent síncrono ainda existe, mas userEvent é preferido para novos testes.
Utilitários assíncronos (waitFor, findBy*) consultam até que as asserções passem ou expirem - eles substituem malabarismos manuais com setTimeout + act.
getBy* lança um erro se houver zero ou muitas correspondências. queryBy* retorna null quando ausente (bom para asserções negativas). findBy* envolve waitFor + getBy* para elementos que aparecem após trabalho assíncrono.
// Elemento aparece após o fetch - prefira findBy*expect(await screen.findByText("Welcome back")).toBeOnTheScreen();// Callback ou mock invocado após o processamento do estadoawait waitFor(() => { expect(onSubmit).toHaveBeenCalled();});// Múltiplas asserções após uma interaçãoawait user.press(screen.getByRole("button", { name: "Refresh" }));await waitFor(() => { expect(screen.getByText("Updated")).toBeOnTheScreen();});
O tempo limite padrão de waitFor é de 1000 ms - passe { timeout: 3000 } para redes simuladas lentas, não para tempo de produção.
Referências de elementos em cache após re-renderização - Nós obsoletos de antes de uma atualização de estado causam inconsistências de "elemento não encontrado". Correção: Reconsulte com screen.getBy* dentro de waitFor, ou escopo as consultas para a renderização mais recente.
Falta de await em userEvent - Pressionar e digitar são assíncronos; esquecer await causa corridas nas asserções. Correção:await user.press(...) e await user.type(...) em todos os testes.
getByTestId como padrão - testID é invisível para os usuários e se desvia das alterações de cópia. Correção: Adicione accessibilityLabel / accessibilityRole nos componentes; reserve testID para itens de lista ou casos extremos de driver nativo.
fireEvent para fluxos complexos - fireEvent.press ignora o pipeline de eventos que userEvent exercita. Correção: Prefira userEvent.setup() para interações; mantenha fireEvent para casos raros de baixo nível.
Testando detalhes de implementação - Afirmar component.state.count ou renderizações rasas no estilo enzyme quebram em refatorações. Correção: Afirme resultados visíveis: texto, roles, callbacks, estado de acessibilidade.
Itens FlatList não encontrados - Listas virtualizadas podem não montar linhas fora da tela. Correção: Passe pequenos arrays data em testes, defina initialNumToRender alto em um wrapper de teste ou teste componentes de linha isoladamente.
Não importar RNTL na configuração do Jest - Matchers como toBeOnTheScreen lançam "matcher não encontrado". Correção:import "@testing-library/react-native" em jest.setup.ts (veja Configuração do Jest para Expo).
Mesma filosofia - consulte pelo que os usuários veem. As APIs se alinham (render, screen, userEvent), mas RN usa accessibilityRole, accessibilityLabel e Pressable em vez de roles DOM e click.
Eu preciso de @testing-library/jest-native?
Não para novos projetos. RNTL registra matchers (toBeOnTheScreen, toHaveAccessibilityState) quando você importa de @testing-library/react-native. @testing-library/jest-native é legado.
getByText vs getByRole para botões?
Prefira getByRole("button", { name: "Sign in" }) - ele verifica se o controle é exposto como um botão para tecnologia assistiva. getByText("Sign in") sozinho não confirma a capacidade de clique.
Quando devo usar findBy*?
Quando o elemento aparece após trabalho assíncrono - dados buscados, useEffect, transição de navegação. findBy* combina waitFor + getBy* e retorna uma promessa que você await.
Use queryBy* - getBy* lança um erro quando o elemento está ausente.
Posso testar hooks personalizados sem renderizar uma tela?
Sim - renderHook de @testing-library/react-native envolve o hook em um harness de teste. Use act() quando o hook atualiza o estado de forma síncrona.
Como testo as mudanças do TextInput?
const user = userEvent.setup();await user.type(screen.getByLabelText("Email"), "alex@example.com");
Ou fireEvent.changeText para controle direto quando não estiver simulando pressionamentos de tecla. Prefira userEvent para testes centrados no usuário.
Por que meu teste FlatList não encontra uma linha?
A virtualização pode não montar itens fora da tela - mantenha data pequeno nos testes.
Extraia o componente de linha e teste-o diretamente com props.
Certifique-se de que keyExtractor retorne chaves estáveis para que as células reconciliem de forma previsível.
Como testo modais e UI condicional?
Abra o modal via userEvent.press no gatilho, então await waitFor ou findBy* para o conteúdo do modal. Consulte dentro do modal por role/texto - não por nome de portal (RN não tem portal DOM).
Devo usar screen ou desestruturar de render?
screen é preferível - as consultas sempre visam a renderização mais recente sem passar getByText adiante. Desestruturar de render() é bom para testes de consulta única.
Use mapas de rotas inline ou diretórios de fixtures; veja a documentação do renderRouter do Expo para padrões de substituição.
E sobre MSW ou mocking de fetch?
Faça mock na fronteira que seu componente usa - jest.mock no módulo da API, ou MSW no Node para testes do tipo integração. Os apresentadores devem receber dados via props para que os mocks de fetch permaneçam nos testes de contêiner.
fireEvent vs userEvent - qual vence?
userEvent para novos testes - ele modela a entrada sequencial do usuário. fireEvent é de nível mais baixo e pode ocultar bugs de await ausentes quando misturado com atualizações de estado assíncronas.
Como depuro a árvore renderizada?
import { render, screen, debug } from "@testing-library/react-native";render(<MyComponent />);screen.debug(); // imprime a árvore de host no console
Use com moderação - prefira falhas explícitas de queryBy* que dizem o que está na tela.