Two Kinds of Tests for a Modern Frontend
9 min read
Automated tests catch regressions before users do. But "testing" is not one thing — logic tests and UI tests catch different bugs and run at different speeds.
Vocabulary
| Layer | Tool | Speed | Catches |
|---|---|---|---|
| Unit / logic | Vitest + jsdom | Milliseconds | Wrong output from pure functions |
| UI / interaction | Storybook + Chromium | Seconds | Broken renders, dead click handlers |
Steps
Step 1 — Configure Vitest with two projects
typescript
Step 2 — Write a unit test
Place it next to the module it tests:
typescript
Run: yarn test:unit
Step 3 — Write a server utility test
SEO builders and sitemap generators are perfect unit-test targets:
typescript
Step 4 — UI tests live in Storybook stories
No separate Button.test.tsx needed — the story's play() function IS the test:
typescript
Run: yarn test:storybook
Step 5 — Run coverage and wire CI
bash
Both projects gate merges in GitHub Actions.
When to use which
| Question | Use |
|---|---|
| Pure function, no DOM? | Vitest unit test |
| Component renders correctly? | Storybook story |
| Click handler fires? | Storybook play() |
| DB row maps to display object? | Vitest unit test |
Common pitfalls
- Testing JSX line-by-line with unit tests — slow and brittle. Use Storybook instead.
- Skipping Storybook tests in CI — broken UI ships silently.
- 100% coverage on components — aim coverage at logic modules.
Verify it works
Run yarn test. Both projects should pass. Break a mapper intentionally — unit test should fail. Break a button handler — Storybook test should fail.
Takeaway
Vitest for brains. Storybook for eyes. Two projects, one CI pipeline, zero excuses for regressions.