Component Tests
5 minute read
Definition
A component test verifies a complete component - either a frontend component rendered in isolation, or a backend service exercised through its public interface - with test doubles replacing all external dependencies. No real databases, downstream services, or network calls leave the process. The test treats the component as a black box: inputs go in through the public interface (API endpoint, rendered UI), observable outputs come out, and the test asserts only on those outputs.
This is broader than a sociable unit test: where a sociable unit test allows in-process collaborators for a single behavior, a component test exercises the entire assembled component through its public interface.
The goal is to verify the assembled behavior of a component - that its modules, business logic, and interface layer work together correctly - without depending on any system the team does not control.
When to Use
- You need to verify a complete user-facing feature from input to output within a single deployable unit.
- You want to test how the UI, business logic, and data layer collaborate without depending on live external services or databases.
- You need to simulate realistic user workflows (filling in forms, navigating pages, submitting API requests) while keeping the test fast and repeatable.
- You are validating acceptance criteria for a user story and want a test that maps directly to the specified behavior.
- You need to verify keyboard navigation, focus management, and screen reader announcements as part of feature verification.
If the test needs a real external dependency (live database, live downstream service), it is an end-to-end test. If it tests a single unit in isolation, it is a unit test.
Characteristics
| Property | Value |
|---|---|
| Speed | Milliseconds to seconds |
| Determinism | Always deterministic |
| Scope | A complete frontend component or backend service |
| Dependencies | All external systems replaced with test doubles |
| Network | Localhost only |
| Database | None or in-memory only |
| Breaks build | Yes |
Examples
Backend Service
A component test for a REST API, exercising the full application stack with the downstream inventory service replaced by a test double:
Frontend Component
A component test exercising a login flow with a mocked authentication service:
Accessibility Verification
Component tests already exercise the UI from the actor’s perspective, making them the natural place to verify that interactions work for all users. Accessibility assertions fit alongside existing assertions rather than in a separate test suite.
Anti-Patterns
- Using live external services: making real network calls to external systems makes the test non-deterministic and slow. Replace everything outside the component boundary with test doubles.
- Using a live database: a live database introduces ordering dependencies and shared state between tests. Use in-memory databases or mocked data layers.
- Ignoring the actor’s perspective: component tests should interact with the system the way a user or API consumer would. Reaching into internal state or bypassing the public interface defeats the purpose.
- Duplicating unit test coverage: component tests should focus on feature-level behavior and happy/critical paths. Leave exhaustive edge case and permutation testing to unit tests.
- Slow test setup: if bootstrapping the component takes too long, invest in faster initialization (in-memory stores, lazy loading) rather than skipping component tests.
- Deferring accessibility testing to manual audits: automated WCAG checks in component tests catch violations on every commit. Quarterly audits find problems that are weeks old.
Connection to CD Pipeline
Component tests run after unit tests in the pipeline and provide the broadest fast, deterministic feedback before code is promoted:
- Local development: run before committing. Deterministic scope keeps them fast enough to run locally without slowing the development loop.
- PR verification: CI executes the full suite; failures block merge.
- Trunk verification: the same tests run on the merged HEAD to catch conflicts.
- Pre-deployment gate: component tests can serve as the final deterministic gate before a build artifact is promoted.
Because component tests are deterministic, they should always break the build on failure. A healthy CD pipeline relies on a strong component test suite to verify assembled behavior - not just individual units - before any code reaches an environment with real dependencies.