Verdict
No
No, do not write assertions whose only job is to check static UI copy such as headings and help text; find elements by role and visible name, and test only the text that the code computes from data.
Why
- Blast radiususers
- Change frequencyregularly
- Detectabilityimmediately
- Reversibilitytrivial
- Test costmoderate
Do not test static copy. For a heading or a help text written literally in a React or Vue component, Blast radius is users, because customers read it, and Change frequency is regularly, because copy edits arrive about once a month. Detectability is immediately: the new wording sits in the diff and on the screen the author opens, and a test would repeat the string the author just typed. Reversibility is trivial, because a revert restores the old text, and Test cost is moderate, because every copy edit also means a test edit. Rule R8 gives Do not test; text computed from data moves Detectability to eventually and gets a test.
| When | Decision | Why |
|---|---|---|
| The code picks the text from data, such as a count with plural forms or an empty-state message | Test minimally: one test that renders one value per text variant and asserts the text shown | Detectability moves to eventually, because the author looks at one state and '1 events' shows for one count only |
| The text is the accessible name of an icon button, set in aria-label and never shown on screen | Test minimally: find the button by role and name in its interaction test | Detectability moves to eventually, because sighted developers never read the label |
| The app ships in several languages, with one translation file per locale | Test: a CI check that every key of the source locale exists in every other locale file | Detectability moves to eventually, because nobody on the team reads every locale, and Test cost falls to trivial |
| The law prescribes the wording, such as the consent text beside a marketing email checkbox | Test mandatory: assert the exact required wording on every form that collects the consent | Blast radius rises to safety-or-legal, because consent collected under other wording can be invalid |
| The copy changes in most weeks, such as the headline of a marketing page | Do not test the wording; review the copy in the pull request and on the preview deployment | Change frequency rises to constantly, but Detectability stays immediately and Reversibility trivial, so the decision holds |
What breaks if you don't test
A test that asserts 'Save changes' fails only when someone changes that string on purpose, so dropping it loses nothing. The text failures that reach customers come from text the code builds: "You have 1 events", an empty state that never appears because a condition flipped, or a raw key such as settings.title on the German page. Computed text breaks in states the author did not open, and customers find the error days later.
What you lose if you over-test
When every label has an assertion, a content designer who rewrites 30 labels fails dozens of tests, and a developer spends hours pasting new strings into them. Copy assertions never fail for a bug, so the team learns to update them without reading, and a real text regression goes through the same way. Tests that find elements by a whole sentence also break when a comma moves.
What to do instead
Review wording in the pull request diff and on the preview deployment. In component tests, find elements as the Testing Library guiding principles describe, by role and accessible name, as in getByRole('button', { name: /save/i }). Test the text that code computes: plural forms, conditional messages, validation errors and formatted dates. If the app has translation files, add one CI check that every locale holds every key.
When the answer changes
- The code chooses between texts, or builds them from numbers and dates.
- The product ships in more than one language.
- A lawyer or a regulator prescribes the wording.
Real incident + Code example
The tone-of-voice update that failed 61 tests
On a team calendar product I worked on, component tests asserted 212 strings copied from the English JSX. A tone-of-voice update changed 38 of them and failed 61 tests, and updating them took a developer most of a day. Nothing checked the locale files, and a release showed the raw key calendar.empty.title to German users for nine days, until a customer sent a screenshot. We deleted the copy assertions, kept the role queries, and added two tests like these (Vitest, jest-dom, flat locale files):
import { render, screen } from '@testing-library/react';
import { expect, test } from 'vitest';
import en from '../locales/en.json';
import de from '../locales/de.json';
import { WeekSummary } from './WeekSummary';
test.each([
[0, 'No events this week'],
[1, '1 event this week'],
[4, '4 events this week'],
])('describes a week with %i events', (count, text) => {
render(<WeekSummary eventCount={count} />);
expect(screen.getByRole('status')).toHaveTextContent(text);
});
test('the German file has every English key', () => {
expect(Object.keys(de).sort()).toEqual(Object.keys(en).sort());
});
The first test fails when a plural form or the empty state breaks, the second when a German key is missing. A reworded heading fails neither.
Sources
Related questions
FAQ
- Should text contents be tested in React tests?
Test the text that a React component computes from props or state, such as plural forms, and skip static copy written literally in the JSX. Find elements by role and accessible name, so wording enters tests only where users rely on it to find a control.
- Should I test translations and i18n keys?
Yes, run one CI check that every key of the source language exists in every locale file. The check takes minutes to write and catches raw keys on screen that a team reading one language would miss.
- How do I stop copy changes from breaking my tests?
Delete assertions whose only job is to check static wording, and find elements by role with a case-insensitive regular expression such as
/save/i. Keep exact text only in tests of computed text, where a changed output is changed behaviour.- Should I test error messages in the UI?
Yes, test that validation errors show their messages, because they appear only on invalid input that the author rarely types. One component test that submits invalid values and finds each message by its key words covers the main path.