Testing strategy and patterns for GeroCare using Vitest, Playwright, and testing utilities. Trigger: When creating tests, test helpers, factories, or working with testing infrastructure.
3f:T2878,
Use this skill when:
Don't use this skill when:
feature-development instead)ui-components instead)coding-style for style, feature-development for structure)This skill works with:
feature-development: Tests are created for features following Clean Architectureui-componentscoding-style: Test code follows same style conventionsTesting Workflow:
feature-development → Create feature structuretesting → Write tests for the featurecoding-style → Ensure test code follows style conventionssrc/**/__tests__/**/*.spec.tse2e/**/*.spec.tssrc/test/factories/ (ResidentFactory, UserFactory)src/test/helpers/ (render, router, auth, firestore)src/test/matchers/ (custom Vitest matchers)src/test/setup.ts (Firebase emulator, global config)Always use factories instead of manually creating test data:
// ✅ GOOD - Use factory function (recommended)
import { createResidentFactory } from '@/test/factories'
const resident = createResidentFactory()
.withAge(75)
.withMedicalInfo({ allergies: ['Peanuts'] })
.build()
// ✅ ALSO GOOD - Legacy object-style API (still supported)
import { ResidentFactory } from '@/test/factories'
const resident = ResidentFactory.create()
.withAge(75)
.withMedicalInfo({ allergies: ['Peanuts'] })
.build()
// ❌ BAD - Manual object creation
const resident = {
id: 'test-id',
firstName: 'Juan',
// ... many more fields
}
Available Factories:
createResidentFactory() - Create Resident entities (functional)createUserFactory() - Create Firebase User objects (functional)ResidentFactory - Legacy object API (delegates to createResidentFactory)UserFactory - Legacy object API (delegates to createUserFactory)Use the render helper for components that need Pinia/Router:
import { renderComponent } from '@/test/helpers/render'
import MyComponent from './MyComponent.vue'
const wrapper = renderComponent(MyComponent, {
props: { resident },
router: true, // Auto-configure router
pinia: true, // Auto-configure Pinia
})
For simple components, use @vue/test-utils directly:
import { mount } from '@vue/test-utils'
const wrapper = mount(MyComponent, { props: { data } })
Firebase emulator is configured automatically in src/test/setup.ts:
import { testDb } from '@/test/setup'
import { createDocument } from '@/test/helpers/firestore'
// Use emulator automatically - no manual mocking needed
const id = await createDocument(testDb, 'residents', residentData)
Mock Firebase Auth when testing auth-related code:
import { vi } from 'vitest'
vi.mock('firebase/auth', () => ({
signInWithEmailAndPassword: vi.fn(),
createUserWithEmailAndPassword: vi.fn(),
}))
Use custom matchers for domain-specific validations:
import { ResidentFactory } from '@/test/factories'
const resident = ResidentFactory.create().withAge(75).build()
expect(resident).toBeValidResident()
expect(resident).toHaveAge(75)
Need to test business logic?
→ Unit test (Vitest)
→ Use factories for test data
→ Mock external dependencies
Need to test Vue component?
→ Component test (Vitest + @vue/test-utils)
→ Use renderComponent() helper if needs Pinia/Router
→ Use mount() directly for simple components
Need to test user interactions/flows?
→ E2E test (Playwright)
→ Test against running app (dev server or preview)
Need to test multiple layers together?
→ Integration test (Vitest)
→ Use Firebase emulator for real Firestore operations
import { describe, it, expect } from 'vitest'
import { createResidentFactory } from '@/test/factories'
import { calculateAge } from '@/business/residents/domain/Resident'
describe('calculateAge', () => {
it('should calculate age correctly', () => {
const resident = createResidentFactory().withAge(75).build()
const age = calculateAge(resident.dateOfBirth)
expect(age).toBe(75)
})
})
import { describe, it, expect } from 'vitest'
import { renderComponent } from '@/test/helpers/render'
import { createResidentFactory } from '@/test/factories'
import ResidentCard from './ResidentCard.vue'
describe('ResidentCard', () => {
it('should display resident name', () => {
const resident = createResidentFactory()
.withName('Juan', 'Pérez')
.build()
const wrapper = renderComponent(ResidentCard, {
props: { resident },
})
expect(wrapper.text()).toContain('Juan Pérez')
})
})
import { describe, it, expect, beforeEach } from 'vitest'
import { testDb } from '@/test/setup'
import { createDocument, getDocumentById } from '@/test/helpers/firestore'
import { createResidentFactory } from '@/test/factories'
describe('Firestore operations', () => {
beforeEach(async () => {
// Cleanup handled automatically in setup.ts
})
it('should create and retrieve resident', async () => {
const resident = createResidentFactory().build()
const id = await createDocument(testDb, 'residents', resident)
const retrieved = await getDocumentById(testDb, 'residents', id)
expect(retrieved).toMatchObject(resident)
})
})
import { test, expect } from '@playwright/test'
test('should navigate to residents list', async ({ page }) => {
await page.goto('/residents')
await expect(page.locator('h1')).toContainText('Residents')
})
# Run all unit tests (watch mode)
npm run test:unit
# Run tests once
npm run test:unit -- --run
# Run tests with coverage
npm run test:unit -- --coverage
# Run specific test file
npm run test:unit -- src/business/residents/__tests__/store.spec.ts
# Run E2E tests
npm run test:e2e
# Run E2E tests in UI mode
npm run test:e2e -- --ui
# Run tests in CI mode (no watch)
npm run test:unit -- --run --coverage
src/test/
├── factories/ # Test data factories
│ ├── ResidentFactory.ts
│ ├── UserFactory.ts
│ └── index.ts
├── helpers/ # Test utilities
│ ├── render.ts # Vue component renderer
│ ├── router.ts # Router mocks
│ ├── auth.ts # Auth helpers (legacy, use factories)
│ └── firestore.ts # Firestore helpers
├── matchers/ # Custom Vitest matchers
│ └── customMatchers.ts
└── setup.ts # Global test setup (Firebase emulator)
src/**/__tests__/ # Unit and integration tests
e2e/ # E2E tests
Firebase emulator is automatically configured in src/test/setup.ts:
localhost:8080testDb from setupUse src/test/helpers/firestore.ts for common operations:
createDocument() - Create document in collectiongetDocumentById() - Retrieve document by IDupdateDocument() - Update documentdeleteDocument() - Delete documentqueryDocuments() - Query with filtersStorybook is planned but not yet configured. When ready:
@storybook/test integration docs// BAD
const resident = { id: 'test', firstName: 'Juan', ... }
// GOOD
const resident = ResidentFactory.create().build()
// BAD (unless necessary for specific test)
vi.mock('firebase/firestore')
// GOOD
import { testDb } from '@/test/setup'
// BAD
const pinia = createPinia()
const router = createRouter(...)
// GOOD
const wrapper = renderComponent(MyComponent, { router: true, pinia: true })