Integration & Coverage

Course 1 · Ch 8
Integration & Coverage
Testing real component wiring, what coverage numbers actually mean, and closing the loop with accessibility

Chapter 1's testing pyramid named "integration tests" as its middle layer, but Chapters 2–7 have stayed mostly at the component level. This chapter finally writes a genuine integration test, then turns to two different topics: what code coverage actually measures (and where it misleads), and the accessibility-testing tool that closes the loop back to web-accessibility1.

From Component Tests to Integration Tests

A component test renders one component, often with mocked props/children. An integration test renders a parent alongside its real children, exercising how they actually communicate — using Chapter 6's MSW to fake the underlying API, since the point is exercising the real component wiring, not a real network:

it("updates results when a search is submitted", async () => { render(<SearchPage />); // contains a REAL SearchForm + a REAL SearchResults await userEvent.type(screen.getByLabelText("Search"), "laptop"); await userEvent.click(screen.getByRole("button", { name: "Search" })); expect(await screen.findByText("3 results for \"laptop\"")).toBeInTheDocument(); });

Neither SearchForm nor SearchResults is mocked — the test proves they genuinely work together, not just individually.

Code Coverage: What It Measures

jest --coverage reports what percentage of lines, branches, functions, and statements were executed during the test run — useful as a rough signal for finding obviously untested code, not as proof of correctness.

Why 100% Coverage Can Be Misleading

A line being executed says nothing about whether it was actually asserted on correctly. Consider a genuinely hollow test:

it("renders", () => { render(<UserProfile user={{ name: "Ada" }} />); // no assertions at all — yet every line UserProfile touches now counts as "covered" });

This test contributes real coverage percentage while catching precisely zero bugs. 100% coverage is neither necessary nor sufficient for a genuinely well-tested codebase — coverage finds obviously neglected code (0% on an entire file is a real red flag), it doesn't validate that the tests which do exist are meaningful.

Setting Sensible Coverage Thresholds

Jest's coverageThreshold config option fails the test run if coverage drops below a set percentage — a practical floor (commonly 70–80%) that catches obviously-neglected code, not a target chased for its own sake. A team mandate of "100% coverage or the PR is blocked" actively incentivizes exactly the hollow test shown above.

Accessibility Testing with jest-axe

jest-axe runs automated accessibility checks — missing labels, invalid ARIA usage, and more — against rendered output, closing the loop directly to web-accessibility1:

import { axe, toHaveNoViolations } from "jest-axe"; expect.extend(toHaveNoViolations); it("has no accessibility violations", async () => { const { container } = render(<SignupForm />); const results = await axe(container); expect(results).toHaveNoViolations(); });

Automated checks like axe catch only a subset of real accessibility problems — structural/markup issues, not things like whether an error message makes logical sense in the order a screen reader announces it. Manual testing, exactly as web-accessibility1 covers, is still necessary — axe is a floor, not a substitute.

Coverage as a Signal vs. Coverage as a Target

As a Signal

Flags obviously untested files/branches — a useful early-warning tool.

As a Target

Chasing 100% actively rewards hollow, assertion-free tests that inflate the number without catching bugs.

Integration Test

A parent rendered with its real children, exercising actual communication between them.

Coverage Report

What percentage of code ran during tests — a signal for gaps, not a correctness proof.

coverageThreshold

A practical floor (e.g. 70–80%) that fails builds below it — not a 100% mandate.

jest-axe

Automated, structural accessibility checks — a floor, not a replacement for manual review.

Accessibility Testing Transfers Across Frameworks Too
axe-core (the engine behind jest-axe) has bindings across the ecosystem — cypress-axe, framework-agnostic axe-core usage in Vue/Angular/Svelte test suites. The same automated-accessibility-as-a-floor approach transfers across frameworks, exactly like Chapter 7's core testing philosophy did.
A Passing jest-axe Check Doesn't Mean "This Is Accessible"
axe only catches automatically detectable issues — missing alt text, invalid ARIA, certain contrast problems. It cannot catch whether an error message's reading order actually makes sense to a screen reader user, or whether a keyboard-only flow is genuinely usable end to end. Treating a green jest-axe result as "done" directly contradicts web-accessibility1's own point: automated tools are a floor, never a ceiling.

Coding Challenges

Challenge 1

Write an integration test for a TodoApp component containing a real AddTodoForm and a real TodoList: typing a new todo and submitting should make it appear in the list, using no mocks for either child component.

📄 View solution
Challenge 2

Explain, with a concrete example different from this chapter's own, how a test could achieve 100% line coverage of a function while still failing to catch an obvious bug in that function's logic.

📄 View solution
Challenge 3

Write a jest-axe test for a component, then describe one realistic accessibility problem it would NOT catch, and briefly explain why axe is structurally unable to detect it.

📄 View solution

Chapter 8 Quick Reference

  • Integration test — a parent rendered with its real children, no mocking of the components themselves
  • jest --coverage — reports line/branch/function/statement execution, not correctness
  • 100% coverage is neither necessary nor sufficient — a hollow, assertion-free test still counts toward it
  • coverageThreshold — set a practical floor (70-80%), not a 100% mandate that incentivizes hollow tests
  • jest-axeaxe(container) + toHaveNoViolations() for automated accessibility checks
  • Automated accessibility checks are a floor — manual testing (per web-accessibility1) is still required
  • Both the coverage-as-target trap and the axe-is-enough trap share one lesson: automated numbers are a signal, not a substitute for real review
  • Next chapter: Capstone — a complete test suite for a real feature, combining every tool from this course