Testing
How We Cut Test Overhead by over 50% with Shared Fixtures
Maintaining test data feels like a drag? Learn how we used a single set of JSON fixtures across Cypress, Vitest, and Mockoon to reduce duplication, speed up development, and ensure consistency in a frontend development and testing workflow.
Wolfgang Rittner
November 19, 2025 · 6 min read
Real-World Vue App
Fixtures are a lifesaver when working with APIs, especially for a production-grade, customer-facing frontend app backed by a custom, non-trivial JSON API. A trusted set of response fixtures saves hours of setup time, avoids dependency on a finicky backend (looking at you, Django), and keeps development smooth. But here’s the catch: testing can’t rely on a real backend either.
For our Vue/Pinia frontend, that means unit tests in Vitest, integration and E2E testing in Cypress, and local development mocking with Mockoon (think of it as a lightweight reverse Postman for mocking API endpoints). Each of these needs fixtures.
Duplicated Fixtures, Wasted Time
So what happens? You create fixtures for each tool. Maybe different teams, devs and QA, build their own sets for their own purposes. Before you know it, you’re drowning in duplicates: the same response fixtures, slightly tweaked, scattered across your repo. Everyone’s developing and testing the same features, the same use cases, but with fixtures that are almost identical yet different enough to cause headaches. Maintenance becomes a nightmare, inconsistencies creep in, and suddenly, you’re spending more time managing test data than creating awesome features and relevant tests.
Example:
Your app has a /user endpoint. There’s a fixture for Vitest, another for Cypress, and a third for Mockoon. All three describe the same endpoint, the same user. Then the API adds an is_active flag. It’s added to the Vitest fixture, missed in the Cypress one, and the Mockoon fixture spells it wrong as isActive. Easy to fix? Sure. But now you’re stuck:
- Which one is correct?
- Who’s responsible for debugging and fixing it?
- And what about the other three inconsistencies you just spotted while digging through the fixtures? 😭
One Set of Fixtures for All Tools
We decided to centralize all request/response fixtures in a single directory (e.g. /fixtures). Instead of maintaining separate copies for each tool, and needing various different teams and people to create their own fixtures from scratch all the time, we reuse the same JSON files everywhere:
- Cypress (E2E tests)
- Mockoon (local API mocking for development and quick demos)
- Vitest/MSW (unit/integration tests)
This way, any change to the API (like adding is_active) only needs to be updated once, in one place. Chances are, this is done right away by the developer working on adding support for that new field. They need to create unit tests, so they need accurate response fixtures, right?
But it doesn’t always flow that way. QA might need more diverse use cases for E2E testing, so they create additional fixtures as needed. If they uncover an unexpected issue, a dev is assigned to fix the underlying bug. Now, if the dev needs to reproduce the issue in a unit test, the fixtures are already there, because QA created them. Bam: teamwork!
Implementation
Directory Structure
All fixtures live in a single, shared directory:
/fixtures
├── users.json
├── products.json
└── ...How It Works
- Cypress: Load fixtures with
cy.fixture('users.json'). - Mockoon: Import JSON files directly into your mock endpoints.
- Vitest/MSW: Reference the same files in your tests or handlers.
No syncing, no duplicates. Update a fixture once, and every tool uses the latest version.
For teams using MSW, you could even reuse the same handler definitions in both Vitest and Cypress. Or, if you’re feeling adventurous, replace Mockoon with MSW for local development; though that might be a step too far, your call.
Less Overhead, More Collaboration
1. Everyone Works from the Same Data
Gone are the days of devs and QEs maintaining separate fixture silos. With a shared /fixtures directory, both teams work from the same data, and there's no more confusion about which version is correct. No more last-minute surprises and back-and-forth when a test fails because of an outdated mock.
2. Maintenance Becomes a Team Sport
- A dev adds a new API field? They update the fixture once, and QE's E2E tests automatically use the latest version.
- QE needs to test an edge case? They add a fixture, and devs reuse it for unit tests. No Duplication. No wasted effort.
- Shared ownership means fewer bottlenecks and faster iterations.
3. Debugging Gets Easier
When QA finds an issue in Cypress, the fixtures to reproduce it in Vitest are already there. Devs and QEs debug with the same data, cutting down on back-and-forth and "works on my machine" arguments. Issues get resolved faster because everyone's literally on the same page.
4. Your System Scales with Your Team
- New endpoint? Update
/fixturesonce, and all tests and mocks stay in sync. - Onboarding becomes easier: New hires, whether devs or QEs, learn one fixture system, not three.
- Need to test a complex workflow? The fixtures are already there, created collaboratively and trusted by everyone.
Try it Yourself
If your team struggles with fixture sprawl or communication gaps between devs and QEs, start small: Move one endpoint's fixtures to a shared directory. Measure the difference in maintenance time, bug reports, and most importantly how much smoother collaboration feels.
Why It Works
Shared fixtures don’t just cut maintenance in half—they clarify how APIs work for everyone. With one source of truth, there’s less confusion, fewer questions about "which fixture is correct," and more confidence in the data. Devs and QE naturally align on how features behave, and the team spends less time double-checking and more time building.
The result? Half the effort, twice the clarity. Start with one endpoint, and see the difference.
If you’re curious how this could work for your team, or want a hand setting it up, I’m happy to help.
FAQs
Q: What if different tests need different variations of the same fixture?
A: You don't need one monolithic file. Create multiple fixtures for different scenarios: users-valid.json, users-expired.json, users-edge-cases.json. They all live in /fixtures with consistent naming. Tests load whichever file they need. You get flexibility while maintaining a single source of truth—no tool-specific duplicates.
Q: We already have separate fixtures scattered everywhere. How do we migrate?
A: Start small. Pick one high-traffic endpoint and consolidate just those fixtures. Run old and new in parallel during the transition to ensure nothing breaks. Once that's working and the team sees the benefits, migrate the next endpoint. This incremental approach minimizes risk and builds confidence as you go.
Q: Won't this create merge conflicts when multiple people update the same fixture?
A: Yes, occasionally—but that's actually better than the alternative. When two people modify the same shared fixture, Git flags the conflict immediately and you resolve it in seconds. With separate fixtures, two people independently update vitest/users.json and cypress/users.json with conflicting data. Git sees no problem, but now your tests contradict each other. You won't discover it until later when tests are flaky or QA can't reproduce a dev's issue. Shared fixtures make conflicts visible and trivial. Separate fixtures make them silent and painful.