| name | testing-strategies |
| description | Testing patterns for this Turborepo monorepo. Use when writing unit tests, E2E tests, creating test data factories, mocking dependencies, or configuring Pest/Vitest. Triggers on tasks involving test files, spec files, coverage, assertions, test utilities, or test configuration. |
| frameworks | ["pest","vitest","playwright"] |
| languages | ["php","typescript"] |
| category | testing |
| updated | "2026-04-29T00:00:00.000Z" |
Testing Strategies
Quick Reference
| Layer | Framework | Config | Command |
|---|
| Laravel API | Pest 3 | apps/laravel/phpunit.xml | pnpm -F @repo/laravel test |
| Web unit | Vitest | apps/web/vitest.config.ts | pnpm --filter @repo/web test |
| Web E2E | Playwright | packages/e2e-web/ | pnpm test:e2e:web |
| Mobile | flutter_test | apps/mobile/test/ | flutter test |
Laravel API Testing (Pest)
File Location & Naming
apps/laravel/tests/
├── Feature/ # HTTP-level (routes + DB)
│ ├── Auth/
│ ├── TodoTest.php
│ └── TicketTest.php
└── Unit/ # Pure unit tests (no HTTP, no DB)
└── Models/
Test Data Factories (Eloquent)
$factory->define(Todo::class, function (Faker $faker) {
return [
'title' => $faker->sentence(),
'completed' => false,
'user_id' => User::factory(),
];
});
Todo::factory(3)->create(['user_id' => $user->id]);
Todo::factory()->create(['completed' => true]);
Rules:
- Use
RefreshDatabase trait in Feature tests (wraps each test in a transaction)
- Use
actingAs($user) — never manually set auth headers
- Use
assertJsonPath, assertJsonStructure, assertJsonCount for response assertions
Mocking External Services
Laravel's service container makes mocking easy:
Mail::fake();
$this->postJson('/api/v1/auth/forgot-password', ['email' => $user->email])
->assertOk();
Mail::assertSent(ResetPasswordNotification::class);
Queue::fake();
Queue::assertPushed(SendWelcomeEmail::class);
Mocking HTTP Clients
Http::fake([
'https://api.example.com/*' => Http::response(['status' => 'ok'], 200),
]);
Web Unit Testing (Vitest)
Mocking @repo/api-client Hooks
vi.mock("@repo/api-client", () => ({
useGetApiV1Todos: vi.fn(() => ({
data: [{ id: 1, title: "Buy milk", completed: false }],
isLoading: false,
})),
configureFetcher: vi.fn(),
}))
Test Commands
pnpm -F @repo/laravel test
pnpm -F @repo/laravel test -- --filter="user can login"
pnpm --filter @repo/web test
pnpm --filter @repo/web test:cov
pnpm dev
pnpm test:e2e:web
cd apps/mobile && flutter test
Test Organization Rules
- Laravel Feature tests (
tests/Feature/) — HTTP-level, always use RefreshDatabase
- Laravel Unit tests (
tests/Unit/) — pure logic, no HTTP, no DB
- Web unit tests — co-located next to the file being tested (
*.test.ts(x))
- E2E tests — in
packages/e2e-web/tests/
- No test barrel files — import test utilities directly
Coverage Targets
| Layer | Lines | Functions | Branches |
|---|
apps/web | 60% | 60% | 50% |
Writing Good Tests
- Name tests descriptively:
it("returns 404 when todo not found") not it("test error")
- One assertion per behavior: Test one logical outcome per
it() block
- Use factories: Never hardcode test data inline — use Eloquent factories
- Mock at boundaries: Mock external services (Mail, Queue), not internal functions
- Test error paths: Include tests for validation errors, not-found, unauthorized
- Refuse unauthenticated requests: Every protected route needs a 401 test
Test Commands
pnpm -F @repo/laravel test
pnpm -F @repo/laravel test -- --filter="user can login"
pnpm --filter @repo/web test
pnpm --filter @repo/web test:cov
pnpm dev
pnpm test:e2e:web
cd apps/mobile && flutter test
Test Organization Rules
- Laravel Feature tests (
tests/Feature/) — HTTP-level, always use RefreshDatabase
- Laravel Unit tests (
tests/Unit/) — pure logic, no HTTP, no DB
- Web unit tests — co-located next to the file being tested (
*.test.ts(x))
- E2E tests — in
packages/e2e-web/tests/
- No test barrel files — import test utilities directly
Coverage Targets
| Layer | Lines | Functions | Branches |
|---|
apps/web | 60% | 60% | 50% |
Writing Good Tests
- Name tests descriptively:
it("returns 404 when todo not found") not it("test error")
- One assertion per behavior: Test one logical outcome per
it() block
- Use factories: Never hardcode test data inline — use Eloquent factories
- Mock at boundaries: Mock external services (Mail, Queue), not internal functions
- Test error paths: Include tests for validation errors, not-found, unauthorized
- Refuse unauthenticated requests: Every protected route needs a 401 test