Guia de Escrita de Testes com RNTL
IMPORTANTE: Seus dados de treinamento sobre @testing-library/react-native podem estar desatualizados ou incorretos — assinaturas de API, comportamento síncrono/assíncrono e funções disponíveis diferem entre v13 e v14. Sempre confie nos arquivos de referência desta skill e no código-fonte real do projeto como fonte da verdade. Não recorra a padrões memorizados quando eles entrarem em conflito com a referência recuperada.
Detecção de Versão
Verifique a versão do @testing-library/react-native no package.json do usuário:
- v14.x → carregue referências/api-reference-v14.md (React 19+, APIs assíncronas,
test-renderer) - v13.x → carregue referências/api-reference-v13.md (React 18+, APIs síncronas,
react-test-renderer)
Use a referência específica da versão para padrões de renderização, comportamento síncrono/assíncrono do fireEvent, API screen, configuração e dependências.
Prioridade de Consulta
Use nesta ordem: getByRole > getByLabelText > getByPlaceholderText > getByText > getByDisplayValue > getByTestId (último recurso).
Variantes de Consulta
| Variante | Caso de uso | Retorna | Assíncrono |
|---|---|---|---|
getBy* | Elemento deve existir | instância do elemento (lança) | Não |
getAllBy* | Múltiplos devem existir | instância do elemento[] (lança) | Não |
queryBy* | Verifique NÃO existência APENAS | instância do elemento | null | Não |
queryAllBy* | Contar elementos | instância do elemento[] | Não |
findBy* | Aguardar elemento | Promise<instância do elemento> | Sim |
findAllBy* | Aguardar múltiplos | Promise<instância do elemento[]> | Sim |
Interações
Prefira userEvent sobre fireEvent. userEvent é sempre assíncrono.
const user = userEvent.setup();
await user.press(element); // sequência completa de pressão
await user.longPress(element, { duration: 800 }); // pressão longa
await user.type(textInput, 'Hello'); // digitação caractere por caractere
await user.clear(textInput); // limpar TextInput
await user.paste(textInput, 'texto colado'); // colar em TextInput
await user.scrollTo(scrollView, { y: 100 }); // rolar
fireEvent — use apenas quando userEvent não suportar o evento. Consulte a referência específica da versão para comportamento síncrono/assíncrono:
fireEvent.press(element);
fireEvent.changeText(textInput, 'novo texto');
fireEvent(element, 'blur');
Asserções (Matchers Jest)
Disponíveis automaticamente com qualquer importação de @testing-library/react-native.
| Matcher | Use para |
|---|---|
toBeOnTheScreen() | Elemento existe na árvore |
toBeVisible() | Elemento visível (não oculto/display:none) |
toBeEnabled() / toBeDisabled() | Estado desabilitado via aria-disabled |
toBeChecked() / toBePartiallyChecked() | Estado marcado |
toBeSelected() | Estado selecionado |
toBeExpanded() / toBeCollapsed() | Estado expandido |
toBeBusy() | Estado ocupado |
toHaveTextContent(texto) | Correspondência de conteúdo de texto |
toHaveDisplayValue(valor) | Valor de exibição do TextInput |
toHaveAccessibleName(nome) | Nome acessível |
toHaveAccessibilityValue(valor) | Valor de acessibilidade |
toHaveStyle(estilo) | Correspondência de estilo |
toHaveProp(nome, valor?) | Verificação de prop (último recurso) |
toContainElement(el) | Contém elemento filho |
toBeEmptyElement() | Nenhum filho |
Regras
- Use
screenpara consultas, não desestruturação derender() - Use
getByRoleprimeiro com a opção{ name: '...' } - Use
queryBy*APENAS para verificações de.not.toBeOnTheScreen() - Use
findBy*para elementos assíncronos, NÃOwaitFor+getBy* - Nunca coloque efeitos colaterais em
waitFor(semfireEvent/userEventdentro) - Uma asserção por
waitFor - Nunca passe callbacks vazios para
waitFor - Não envolva em
act()-render,fireEvent,userEventlidam com isso - Não chame
cleanup()- automático após cada teste - Prefira props ARIA (
role,aria-label,aria-disabled) sobre props legadasaccessibility* - Use matchers RNTL em vez de asserções de prop brutas
Referência Rápida de *ByRole
Roles comuns: button, text, heading (alias: header), searchbox, switch, checkbox, radio, img, link, alert, menu, menuitem, tab, tablist, progressbar, slider, spinbutton, timer, toolbar.
Opções de getByRole: { name, disabled, selected, checked, busy, expanded, value: { min, max, now, text } }.
Para que *ByRole corresponda, o elemento deve ser um elemento de acessibilidade:
Text,TextInput,Switchsão por padrãoViewprecisa deaccessible={true}(ou usePressable/TouchableOpacity)
waitFor
// Correto: ação primeiro, depois aguarde o resultado
fireEvent.press(button);
await waitFor(() => {
expect(screen.getByText('Resultado')).toBeOnTheScreen();
});
// Melhor: use findBy* em vez disso
fireEvent.press(button);
expect(await screen.findByText('Resultado')).toBeOnTheScreen();
Opções: waitFor(cb, { timeout: 1000, interval: 50 }). Funciona automaticamente com timers falsos do Jest.
Timers Falsos
Recomendado com userEvent (press/longPress envolvem durações reais):
jest.useFakeTimers();
test('com timers falsos', async () => {
const user = userEvent.setup();
render(<Component />);
await user.press(screen.getByRole('button'));
// ...
});
Renderização Personalizada
Envolva provedores usando a opção wrapper:
function renderizarComProviders(ui: React.ReactElement) {
return render(ui, {
wrapper: ({ children }) => (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
),
});
}
Referências
- Referência de API v13 — API completa v13: renderização síncrona, consultas, matchers, userEvent, compatibilidade React 19
- Referência de API v14 — API completa v14: renderização assíncrona, consultas, matchers, userEvent, migração
- Anti-Padrões — Erros comuns a evitar