Prompt
Write a test for a Counter component using React Native Testing Library. Test that pressing the + button increments the displayed count.
Solution
React Native Testing Library's design principle is the whole answer: test through the UI contract — what the user sees and does — never through implementation details. This test renders the component, simulates the press a user would make, and asserts on the rendered text. It never touches useState, never reads component internals. That's why it survives refactors: swap useState for useReducer or a store and the test stays green, because the behavior it specifies didn't change. A test that asserts on internal state does the opposite — it breaks on refactors and misses real regressions in what renders.
This is the middle layer of the testing pyramid: many Jest unit tests for pure logic below it, few Detox/Maestro E2E flows above it, and RNTL integration tests carrying the "does the component actually behave" load at a fraction of E2E cost.
Query choice is a signal too: RNTL's guidance prefers user-facing queries — getByText, getByRole, accessibility labels — over testID, because those queries fail when the accessible experience breaks. testID is the pragmatic fallback for elements with no user-visible handle.
Red flag: asserting on state values or snapshot-testing the whole tree instead of asserting the visible outcome — both couple the test to internals.
Say it: "RNTL tests assert through the UI contract — press the button, expect the rendered text — so refactoring internals never breaks them and real regressions always do."