Skip to content

Quiz: Write the Right Test

intermediate12 min read

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.

Mental Model

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.

Key Rules
  1. 1Query priority: getByRole > getByLabelText > getByPlaceholderText > getByText > getByDisplayValue > getByAltText > getByTitle > getByTestId
  2. 2Use findBy for async elements, getBy for sync, queryBy when asserting absence
  3. 3Mock at the network boundary (MSW), not at the module level, unless you have a specific reason
  4. 4A flaky test is worse than no test — it erodes trust in the entire suite
  5. 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" />
Quiz
Which Testing Library query is the best choice for this input?

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?

Quiz
How do you assert that an element is NOT in the document?

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);
});
Quiz
Why is this test flaky?

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;
}
Quiz
What type of test is most appropriate for useDebounce?

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?

Quiz
What is the best way to handle API calls in a component integration 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');
});
Quiz
What is the primary problem with this test?

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?

Quiz
Which pattern correctly tests async loading states?

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; }
Quiz
What causes this test isolation failure?

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?

Quiz
How should the checkout flow be tested?

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?

Quiz
What is the correct approach to make this test deterministic?

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();
});
Quiz
What is fundamentally wrong with this test?

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?

Quiz
Which testing approach catches visual layout regressions across viewports?

Scoring Guide

ScoreAssessment
11-12You have elite testing judgment. You know not just how to test, but what to test and at which level.
8-10Strong foundations with a few gaps. Review the explanations for questions you missed — there's a pattern in what tripped you up.
5-7You understand the mechanics but the strategy needs work. Revisit testing philosophy and the query priority docs.
0-4Go back through the module from the beginning. Focus on why each testing pattern exists, not just the API.
What developers doWhat 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