Vuesion tests behavior rather than implementation details.
Tests should provide confidence that the application works as users and external clients experience it. They should not become coupled to private component state, internal method calls, or incidental implementation choices.
Vuesion combines different testing techniques to verify behavior at every layer of the application.
Utility
│
▼
Component
│
▼
API
│
▼
User journey
Unit
│
▼
Integration
│
▼
End-to-end
Different testing approaches answer different questions, but together they provide confidence that the application behaves correctly from individual functions to complete user journeys.
Tests live next to the files they verify.
VueButton.vue
VueButton.stories.ts
VueButton.spec.ts
index.get.ts
index.get.spec.ts
This keeps implementation and tests close together and avoids switching constantly between separate source and test directory trees.
Colocation also makes ownership immediately visible. When a file changes, its corresponding tests are easy to find.
Frontend tests use Vitest together with Testing Library.
They interact with components through the same visible interface available to users:
A test should prefer:
await ui.click(ui.getByRole('button', { name: 'Save' }));
over assertions against internal component state or private methods.
This allows components to be refactored without rewriting tests as long as their observable behavior remains unchanged.
Component scenarios are first defined as Storybook stories.
Once the behavior of a story is satisfactory, its arguments are reused as the starting point for the automated test.
Component
│
▼
Story args
├── Interactive documentation
├── Manual review
└── Automated test setup
This avoids maintaining one set of example data for Storybook and another for Vitest.
A component test imports the relevant story and passes its args to the shared component harness:
import { Identify } from './AuthForm.stories';
import { getHarnessForComponent } from '@test/test-utils';
describe('AuthForm.vue', () => {
it('renders the identify form', () => {
const ui = getHarnessForComponent(AuthForm, {
props: { ...Identify.args },
});
expect(ui.getByLabelText('input.email.label*')).toBeTruthy();
expect(ui.getByText('common.Continue')).toBeTruthy();
});
});
The story defines the visible scenario, while the test verifies its behavior through the same public interface available to users.
Stories remain focused on representative component states. Tests extend those states with interactions and assertions.
This keeps documentation and test setup aligned without duplicating component scenarios.
Server tests verify complete HTTP request flows.
Instead of calling controllers or services directly, tests send real requests through the server API.
A typical server test covers:
HTTP request
│
▼
Input validation
│
▼
Controller
│
▼
Authorization policy
│
▼
Service
│
▼
Prisma
│
▼
PostgreSQL
This verifies that the layers work together rather than proving only that an isolated function returns an expected value.
Server tests use a real PostgreSQL database. Test data is created specifically for the scenario being tested and removed or isolated through the test setup.
While small unit tests remain useful for isolated utilities and domain logic, most application behavior is verified through integration tests.
Backend tests should create the data required by their own scenario.
Avoid relying on one shared demo user or a large global fixture whose state is modified by many unrelated tests.
Tests should be independent from one another.
Running a single test or the entire suite should produce the same result.
Local test data provides:
Factories and helper functions may reduce repetitive setup, but the test should still make its relevant state understandable.
End-to-end tests use Playwright to verify complete user journeys in a real browser.
They run against the complete application and a dedicated E2E database that is separate from the database used by server integration tests.
Typical E2E scenarios include:
Vuesion intentionally keeps the included E2E suite small.
End-to-end testing strategy depends heavily on the product, team, deployment process, and acceptable execution time. Projects should extend the suite around their own most important user journeys rather than attempting to reproduce every lower-level test in the browser.
Vuesion prefers real integrations whenever they remain reliable and reasonably fast.
That means using:
Mocks are still necessary at some external boundaries, such as third-party APIs, email delivery, OAuth providers, browser APIs, or services that cannot safely run during tests.
The goal is not to eliminate mocking.
The goal is to mock the boundaries of the system, not the system itself.
Vuesion aims to maintain 100% code coverage.
This is not about reaching an arbitrary number.
It is about making every uncovered line an intentional decision.
If code should temporarily remain uncovered, that decision should be visible directly in the source code:
/* v8 ignore start */
Explicit coverage exclusions can easily be found and reviewed later through a simple code search.
The goal is not to claim that every test is equally valuable.
The goal is to prevent untested code from accumulating invisibly as the project grows.
Tests become difficult to maintain when they are separated from their source files, coupled to implementation details, dependent on shared mutable fixtures, or built mostly from mocks.
Vuesion instead favors:
The result is a test suite that supports refactoring, encourages confidence, and catches integration problems without requiring every scenario to be repeated at every layer.
Continue with Generators to learn how Vuesion creates consistent foundations for domains, APIs, components, and pages.