Quiz: Write the Right Test
Before You Start
This quiz isn't about memorizing APIs. It's about testing judgment — the kind that separates engineers who write tests that catch real bugs from engineers who write tests that pass today and break tomorrow.
Every question presents a realistic scenario you'd encounter in production codebases. Think through why before picking your answer.
The Testing Trophy prioritizes tests by the confidence they give you per unit of effort. From bottom to top: static analysis (TypeScript, ESLint) catches typos and type errors for free. Unit tests verify isolated logic fast. Integration tests catch how pieces work together — this is where most of your tests should live. E2E tests verify critical user flows end-to-end but are slow and expensive to maintain. The goal isn't coverage numbers. The goal is confidence that your app works for real users.
- 1Query priority: getByRole > getByLabelText > getByPlaceholderText > getByText > getByDisplayValue > getByAltText > getByTitle > getByTestId
- 2Use findBy for async elements, getBy for sync, queryBy when asserting absence
- 3Mock at the network boundary (MSW), not at the module level, unless you have a specific reason
- 4A flaky test is worse than no test — it erodes trust in the entire suite
- 5Test behavior, not implementation — if you refactor and the test breaks but the feature still works, the test was wrong
Question 1: Pick the Right Query
You have a login form with an email input. The input has a visible label "Email address" and type="email". Which query should you use?
<label htmlFor="email">Email address</label>
<input id="email" type="email" name="email" />
Question 2: Asserting Absence
After a user dismisses a notification toast, you want to verify it's no longer in the DOM. Which approach is correct?
Question 3: Spot the Flaky Test
This test passes locally but fails randomly in CI. Why?
test('shows search results', async () => {
render(<SearchPage />);
const input = screen.getByRole('searchbox');
await userEvent.type(input, 'react hooks');
const results = screen.getAllByRole('listitem');
expect(results.length).toBeGreaterThan(0);
});
Question 4: Unit, Integration, or E2E?
You've built a useDebounce hook that delays updating a value until the user stops typing. Where does this test belong?
function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
Question 5: Mock vs. Real
Your component fetches user data from /api/users and displays it. How should you handle the API call in your test?
Question 6: What's Wrong With This Test?
test('counter increments', () => {
const { container } = render(<Counter />);
const button = container.querySelector('.increment-btn');
fireEvent.click(button!);
const display = container.querySelector('.count-display');
expect(display?.textContent).toBe('1');
});
Question 7: Async Patterns
You're testing a component that shows a loading spinner, then replaces it with content after an API call resolves. Which approach correctly handles the async flow?
Question 8: Test Isolation
Two tests share a module that stores state in a module-level variable. Test A passes when run alone but fails when Test B runs first. What's wrong?
// userStore.ts
let currentUser: User | null = null;
export function setUser(user: User) { currentUser = user; }
export function getUser() { return currentUser; }
Question 9: Choosing the Right Level
You have a checkout flow: user adds items to cart, enters shipping info, enters payment info, and sees a confirmation page. Which testing strategy makes sense?
Question 10: Mocking Boundaries
You're testing a component that uses Date.now() to show relative timestamps ("2 hours ago"). Tests pass today but will fail tomorrow. How should you fix this?
Question 11: The Over-Mocked Test
test('user profile loads correctly', () => {
vi.mock('./useUser', () => ({
useUser: () => ({ name: 'Alice', email: 'alice@test.com' })
}));
vi.mock('./Avatar', () => ({
Avatar: ({ name }: { name: string }) => <div>{name}</div>
}));
vi.mock('./EmailLink', () => ({
EmailLink: ({ email }: { email: string }) => <a>{email}</a>
}));
render(<UserProfile userId="123" />);
expect(screen.getByText('Alice')).toBeInTheDocument();
});
Question 12: Visual Regression
Your team's button component looks correct on desktop but the padding breaks on mobile viewports. Unit tests and integration tests all pass. What kind of test catches this?
Scoring Guide
| Score | Assessment |
|---|---|
| 11-12 | You have elite testing judgment. You know not just how to test, but what to test and at which level. |
| 8-10 | Strong foundations with a few gaps. Review the explanations for questions you missed — there's a pattern in what tripped you up. |
| 5-7 | You understand the mechanics but the strategy needs work. Revisit testing philosophy and the query priority docs. |
| 0-4 | Go back through the module from the beginning. Focus on why each testing pattern exists, not just the API. |
| What developers do | What they should do |
|---|---|
| Using getByTestId as the default query for every element data-testid attributes don't verify accessibility. If you query by role, you're also verifying that screen readers can find the element. | Follow the query priority: getByRole first, getByTestId as last resort |
| Mocking every dependency in a component test Over-mocking turns your test into a wiring check. You lose confidence that the real pieces work together. | Mock at the network boundary with MSW, render real child components |
| Using setTimeout or fixed delays to wait for async UI Fixed delays are either too slow (waste CI time) or too fast (flaky). findBy polls the DOM intelligently and resolves as soon as the element appears. | Use findBy queries or waitFor from Testing Library |
| Writing E2E tests for every edge case E2E tests are 10-100x slower than integration tests. Reserve them for flows that span multiple pages and involve real browser behavior. | E2E for critical happy paths, integration tests for edge cases |