Testing and Verification
(Verifying the risks that matter)
Senior rule:
Testing is not about count. Testing is about risk placement.
Atlas has dashboard filters, billing settings, permissions, charts, and export flows. A senior test strategy asks which failures are expensive and which layer catches them with the least brittleness.
Conceptual Model
| Layer | Best for | Avoid |
|---|---|---|
| Unit | pure logic, formatting, reducers | testing framework internals |
| Component | UI behavior in isolation | mocking away the risk |
| Integration | module and data collaboration | giant unclear fixtures |
| Contract | API assumptions | only testing happy paths |
| E2E | critical journeys | covering every detail |
| Visual | layout regressions | unstable screenshots |
| Accessibility | semantics and interaction | assuming automation is enough |
| Performance | budget regressions | lab-only confidence |
Unit Test Example
type Money = { cents: number; currency: "USD" };
export function formatMoney(value: Money) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: value.currency,
}).format(value.cents / 100);
}
Unit tests are strongest when logic is deterministic and isolated.
Playwright E2E Example
import { test, expect } from "@playwright/test";
test("filters reports by region", async ({ page }) => {
await page.goto("/dashboard/reports");
await page.getByLabel("Region").selectOption("Europe");
await page.getByRole("button", { name: "Apply filters" }).click();
await expect(page.getByRole("status")).toHaveText("Report updated");
await expect(page.getByRole("table", { name: "Revenue report" })).toContainText(
"Europe"
);
});
Prefer user-facing locators. They make tests more stable and improve accessibility pressure.
Cross-Browser Projects
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
{ name: "mobile", use: { ...devices["Pixel 7"] } },
],
use: {
trace: "on-first-retry",
},
});
Run all browsers for flows where browser differences are a real product risk. Use a smaller smoke set if full coverage is too expensive.
Avoiding Flakiness
Bad:
await page.waitForTimeout(2000);
await expect(page.locator(".status")).toHaveText("Done");
Better:
await expect(page.getByRole("status")).toHaveText("Done");
Web-first assertions retry until the condition is met or times out.
Contract Test Shape
import { z } from "zod";
const ReportResponse = z.object({
rows: z.array(
z.object({
id: z.string(),
revenueCents: z.number(),
})
),
});
export async function parseReport(response: unknown) {
return ReportResponse.parse(response);
}
This guards frontend assumptions about backend shape. It does not replace backend tests.
Accessibility Verification
Automated checks are useful, but critical flows need manual review:
- keyboard only
- screen reader path
- visible focus
- reduced motion
- zoom and reflow
- form errors
Mini Case Study: Flaky Filter Test
Atlas had a flaky e2e test for report filtering. It used CSS selectors, arbitrary waits, and shared test data.
Fix:
- use role and label locators
- seed data per test
- assert the status message instead of sleeping
- keep each test isolated
- collect traces on retry
Flakiness revealed unclear loading and uncontrolled state.
Common Failure Modes
- E2E tests for logic that belongs in unit tests.
- Unit tests that mirror implementation.
- Tests depending on execution order.
- Arbitrary sleeps.
- Mocking away auth, latency, and error states.
- No accessibility or performance verification.
Review Checklist
- Is the highest product risk tested somewhere?
- Are e2e tests user-visible and resilient?
- Are tests isolated?
- Are API assumptions validated?
- Are error and empty states tested?
- Are accessibility checks included for critical flows?
- Are performance budgets verified?
Senior Deep Dive: Risk-Based Verification
Senior engineers do not ask "do we have enough tests?" They ask "which important failures can still escape, and what is the cheapest reliable place to catch them?"
Risk placement matrix
| Risk | Best verification layer | Why |
|---|---|---|
| pure formatting or calculation | unit | deterministic and cheap |
| component keyboard behavior | component plus accessibility checks | fast and behavior-focused |
| API shape assumption | contract/integration | catches client/server drift |
| permission boundary | integration plus E2E negative path | needs real auth behavior |
| critical purchase/export journey | E2E | proves cross-system user outcome |
| visual layout of stable component | visual regression | catches CSS/design drift |
| performance budget | CI budget plus RUM | lab catches obvious regressions, field proves reality |
| browser compatibility | targeted cross-browser E2E | avoids false confidence from Chromium only |
The weakness this corrects: teams often overuse E2E tests for everything or unit tests for risks that only appear at integration boundaries.
Test design quality
A good test has a clear behavior name, realistic setup, minimum mocking necessary, user-facing interaction where UI is involved, failure messages that point toward the cause, stable selectors based on roles/names where possible, and one meaningful reason to fail.
A weak test snapshots large unstable output, asserts implementation details, mocks away the contract being tested, depends on timing guesses, passes even when the user journey is broken, or fails with no diagnostic signal.
Contract testing for frontend seniors
Frontend contract tests should prove assumptions such as required fields exist, optional fields can be missing, enum values can evolve, error shapes are understood, pagination and sorting semantics match UI expectations, permissions are enforced when UI is wrong, and stale client versions fail safely.
This is where wider software-engineering material matters: good code makes contracts hard to misuse, and architecture makes important contracts explicit.
Flake triage protocol
Do not normalize flakiness. Classify it:
| Flake source | Fix |
|---|---|
| waiting for wrong thing | wait for user-visible state or network contract |
| shared test data | isolate data or reset state |
| animation/timing | disable motion in test or assert final state |
| race in product code | fix product state machine, not the test |
| environment dependency | mock at boundary or improve test environment |
| over-broad E2E | move behavior to lower-level test |
Every ignored flaky test teaches the team that red builds are negotiable.
Verification strategy document
For a major feature, write a short verification strategy: highest-risk journeys, unit/component/integration/E2E coverage choices, what is intentionally not tested and why, accessibility checks, performance budget checks, security and permission checks, production signals after release, and rollback trigger. This makes testing an engineering decision, not a scramble at the end.
Exercises
Exercise 1 - Risk Matrix
Pick one feature and map each risk to a test layer.
Exercise 2 - Rewrite A Flaky Test
Replace CSS selectors and waits with locators and web-first assertions.
Exercise 3 - Critical Flow Verification
For one critical flow, define unit, integration, e2e, accessibility, and performance checks.
Further Reading
- Playwright: Best Practices - https://playwright.dev/docs/best-practices
- Playwright: Locators - https://playwright.dev/docs/locators
- Cypress: Best Practices - https://docs.cypress.io/app/core-concepts/best-practices
- W3C WAI: Testing for accessibility - https://www.w3.org/WAI/test-evaluate/