Testing
Overview
Sutomo uses Pest v4 for PHP testing and Vitest for frontend JavaScript testing. Both are configured with coverage reporting.
Stack
| Layer | Framework | Config | Coverage |
|---|---|---|---|
| PHP (Unit + Feature) | Pest v4 | phpunit.xml | Minimum 75% overall |
| PHP (E2E) | Playwright | playwright.config.ts | — |
| JavaScript (Unit) | Vitest | vitest.config.ts | Minimum 60% |
| JavaScript (E2E) | Playwright | playwright.config.ts | — |
Coverage Standard
| Metric | Minimum Target | Measured By |
|---|---|---|
| Lines | 75% | Pest coverage report |
| Methods | 80% | Pest coverage report |
| Branches | 60% | Pest coverage report |
Coverage reports are generated to tests/coverage-reports/html/ and can be viewed in a browser.
bash
# View coverage report
open tests/coverage-reports/html/index.htmlRunning Tests
PHP Tests (Pest)
bash
# Run all tests
composer run test
# Run specific file
php artisan test --compact --filter=CandidateTest
# Run with coverage
php artisan test --coverage
# Run specific suite
php artisan test --compact tests/Unit
php artisan test --compact tests/FeatureFrontend Tests (Vitest)
bash
# Run all frontend tests
npm run test:unit
# Run specific file
npx vitest run path/to/test.spec.ts
# Watch mode
npx vitestE2E Tests (Playwright)
bash
# Run all E2E tests
npm run test:e2e
# Run with UI mode
npm run test:e2e:ui
# Run specific file
npx playwright test tests/e2e/setup/auto-navigation.spec.tsCI Check
The full CI pipeline runs all checks in order:
bash
composer run ci:check
# Runs: lint → format → types → testWriting PHP Tests
Unit Test Structure
tests/Unit/
├── Traits/ # Tests for shared traits (HasLocalization, HasContact, HasLog)
├── Enums/ # Tests for PHP enums
├── Models/ # Tests for model relationships and scopes
├── Services/ # Tests for service classes
│ ├── Algorithm/
│ ├── Asset/
│ ├── Assistant/
│ └── ...
├── Filament/ # Tests for Filament components
├── Common/ # Tests for common utilities
├── Platform/ # Tests for platform features
├── Mailtrap/ # Tests for email services
└── ExampleTest.phpFeature Test Structure
tests/Feature/
├── Assistant/
├── ContractManagement/
├── Filament/
├── Recruitment/
├── ExampleTest.php
├── RatingFieldTest.php
├── ShieldPermissionTest.php
└── TextareaTest.phpCreating Tests
bash
# Create a feature test
php artisan make:test --pest RecruitmentCandidateTest
# Create a unit test
php artisan make:test --pest --unit Services/Asset/ImageKitServiceTestTest Example (Pest)
php
use function Pest\Laravel\assertDatabaseHas;
it('creates a candidate with valid data', function () {
// Arrange
$data = Candidate::factory()->make()->toArray();
// Act
$response = $this->post(route('api.candidates.store'), $data);
// Assert
$response->assertCreated();
assertDatabaseHas('candidates', ['email' => $data['email']]);
});Test Example (Unit - Service)
php
use App\Services\Otp\Generate;
it('generates a 6-digit code', function () {
$otp = Generate::handle('test@example.com');
expect($otp->code)->toMatch('/^\d{6}$/');
expect($otp->expired_at)->toBeGreaterThan(now());
});Writing Frontend Tests
Store Tests
typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { myStore } from '../stores/my.stores';
describe('myStore', () => {
beforeEach(() => { myStore.reset(); });
it('starts with default state', () => {
expect(myStore.state.data.items).toEqual([]);
});
it('updates on setField', () => {
myStore.setField('name', 'test');
expect(myStore.state.data.name).toBe('test');
});
});Mocking API Calls
typescript
import { vi } from 'vitest';
vi.mock('../api/my.api', () => ({
fetchData: vi.fn(),
}));Key Files
| File | Purpose |
|---|---|
phpunit.xml | PHPUnit/Pest config — databases, coverage, env vars |
vitest.config.ts | Vitest config for frontend unit tests |
playwright.config.ts | Playwright config for E2E tests |
tests/Unit/ | Unit tests (models, services, traits, enums) |
tests/Feature/ | Feature tests (controllers, API endpoints) |
tests/e2e/ | Playwright E2E tests |
tests/coverage-reports/html/ | Coverage report output |