본문으로 건너뛰기

테스트 전략 개요

Multi-SaaS Kit의 테스트 시스템과 전략을 소개합니다.

개요​

Multi-SaaS Kit은 448개 테스트, 1,042개 assertion으로 구성된 견고한 테스트 시스템을 갖추고 있습니다. PHPUnit과 Pest 기반으로 작성되어 있으며, Core 모듈에 대해서는 TDD 접근법을 적용합니다.

┌─────────────────────────────────────────────────────────────┐
│ 테스트 피라미드 │
│ │
│ /\ │
│ / \ E2E 테스트 (선택) │
│ /----\ │
│ / \ │
│ / \ Feature 테스트 (통합) │
│ /----------\ │
│ / \ │
│ / \ Unit 테스트 (단위) │
│ /----------------\ │
│ │
│ Unit : Feature = 7 : 3 비율 권장 │
└─────────────────────────────────────────────────────────────┘

테스트 스택​

도구역할
PHPUnit테스트 프레임워크
PestPHPUnit 래퍼 (간결한 문법)
SQLite In-Memory테스트 데이터베이스
Factory테스트 데이터 생성
RefreshDatabase테스트 간 DB 초기화
테스트 DB 분리

RefreshDatabase는 PHPUnit 실행 중 APP_ENV=testing에서 내부적으로 migrate:fresh를 호출할 수 있습니다. MSK의 DB 안전 가드는 이 테스트 러너 경로만 허용하므로, phpunit.xml과 tests/TestCase에서 운영 DB가 아닌 별도 테스트 DB 또는 SQLite in-memory를 사용하도록 반드시 고정해야 합니다.

테스트 현황​

영역테스트 수Assertions설명
Core/Permission1545권한 시스템 (Level 0~6, ADR-058)
Core/Tenant1751테넌트 격리
Core/Guardian72216후견인 관계 (Extension)
Core/Impersonate66198사용자 전환 (Extension)
Models81243User, Tenant, Organization
Seeder39117CoreSeeder 검증
Filament41123Admin Panel 기능
Security824Cross-Tenant 보안
Total4481,042-

테스트 분류​

Unit 테스트​

단일 클래스/메서드를 격리하여 테스트합니다.

tests/Unit/
├── Core/
│ ├── Permission/ # 권한 로직
│ ├── Tenant/ # 테넌트 스코프
│ ├── Guardian/ # 후견인 관계
│ └── Impersonate/ # 사용자 전환
├── Models/ # 모델 로직
└── Services/ # 서비스 로직

Feature 테스트​

여러 컴포넌트를 통합하여 테스트합니다.

tests/Feature/
├── Core/
│ └── Security/ # Cross-Tenant 보안
├── Filament/ # Admin Panel
├── Http/ # API 엔드포인트
└── Seeder/ # CoreSeeder 검증

테스트 환경 설정​

phpunit.xml​

<phpunit>
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>

TestCase 기본 설정​

// tests/TestCase.php
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
use RefreshDatabase;

protected function setUp(): void
{
parent::setUp();

// 테넌트 기능 비활성화 (Unit 테스트)
config(['core.tenant.enabled' => false]);
}
}

테스트 실행​

전체 테스트​

# 모든 테스트 실행
php artisan test

# 또는 Pest 직접 실행
./vendor/bin/pest

특정 테스트 실행​

# 특정 파일
php artisan test tests/Unit/Core/Permission/HasLevelTest.php

# 특정 폴더
php artisan test tests/Unit/Core/

# 특정 메서드 (필터)
php artisan test --filter=test_user_can_check_level

# 그룹별 실행
php artisan test --group=permission

병렬 실행​

# 병렬 테스트 (빠름)
php artisan test --parallel

# 프로세스 수 지정
php artisan test --parallel --processes=4

커버리지 확인​

# HTML 리포트 생성
php artisan test --coverage-html reports/coverage

# 콘솔 요약
php artisan test --coverage

TDD 적용 원칙​

Multi-SaaS Kit은 Core 모듈에 대해 **TDD (Test-Driven Development)**를 적용합니다.

TDD 필수 영역​

영역이유
Core/Permission권한 오류 = 보안 사고
Core/Tenant데이터 격리 = SaaS 핵심
Core/Audit감사 로그 무결성
비즈니스 로직계산, 상태 전이
보안 관련인증, 인가

TDD 워크플로우​

1. 실패하는 테스트 작성 (Red)
↓
2. 테스트를 통과하는 최소 코드 작성 (Green)
↓
3. 코드 리팩토링 (Refactor)
↓
4. 반복

테스트 헬퍼​

CreatesTestTenant​

테넌트 관련 테스트를 위한 Trait:

use Tests\Traits\CreatesTestTenant;

class MyTest extends TestCase
{
use CreatesTestTenant;

public function test_example(): void
{
// Tenant Admin으로 로그인
$user = $this->actAsTenantAdmin();

// 특정 테넌트 컨텍스트 설정
$this->setTenantContext($tenant);
}
}

Factory 사용​

// 기본 사용자
$user = User::factory()->create();

// 특정 레벨 사용자
$admin = User::factory()->create(['level' => 0]);

// 테넌트와 함께
$tenant = Tenant::factory()
->has(User::factory()->count(3))
->create();

명명 규칙​

테스트 파일​

{테스트대상}Test.php

예:
HasLevelTest.php
TenantScopeTest.php
UserResourceTest.php

테스트 메서드​

// 형식: test_{feature}_{scenario}_{expected_outcome}
test_user_can_check_subscription_status()
test_expired_subscription_returns_false()
test_tenant_user_cannot_see_other_tenant_data()

Pest 스타일​

test('사용자는 구독 상태를 확인할 수 있다', function () {
// ...
});

it('만료된 구독은 false를 반환한다', function () {
// ...
});

멀티세션 격리 테스트 DB​

여러 개발/AI 세션이 동시에 같은 프로젝트를 테스트할 때, 단일 공유 testing DB 의 migrate:fresh(전체 재생성)가 서로 충돌합니다(relation already exists / hang). 이때 세션별 격리 DB(testing_<slug>)를 사용하면 경합 없이 병렬 테스트가 가능합니다.

사용법​

# 1. 격리 DB 생성 (DB명 2번째 인자, 'testing_' prefix 필수)
ops/scripts/create-testing-db.sh exam.how testing_qgs

# 2. 격리 DB 로 테스트 (DB= 파라미터)
make test NAME=exam.how DB=testing_qgs
make test NAME=exam.how DB=testing_qgs ARGS="--filter='SomeTest'"

# 기본(단일 공유)은 그대로
make test NAME=exam.how # DB_DATABASE=testing

동작 원리​

요소동작
이름 규칙testing 또는 testing_* prefix 만 허용
운영 DB 보호TestCase + RLS 마이그레이션 가드가 str_starts_with($db, 'testing') 로 판정 → 운영 DB({project}_db)는 RefreshDatabase 자체가 차단됨
DB 소유권격리 DB OWNER 는 테스트가 연결하는 런타임 role 이어야 migrate:fresh(DDL) 가능
정리사용 종료 후 DROP DATABASE testing_<slug> (운영자)
팁

CREATE DATABASE 는 superuser 권한이 필요합니다. 프로젝트 postgres 의 POSTGRES_USER 가 superuser 가 아니면 운영자가 postgres 로 직접 생성하세요.

CI/CD 통합​

GitHub Actions 예시​

name: Tests

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
coverage: xdebug

- name: Install Dependencies
run: composer install --no-interaction

- name: Run Tests
run: php artisan test --parallel

관련 문서​