테스트 실패 해결
이 저장소에서 반복되는 테스트 인프라 함정 — 컨테이너 네트워킹, 테넌시 라이프사이클, Pest 바인딩, 템플릿 데이터베이스.
테스트 실패 해결
테스트 환경 오류인지 App 회귀인지 증상부터 구분합니다. Assertion 실패까지 테스트 하네스 문제로 단정하지 말고 App 구현을 조사하세요. 권한·테넌트 설정은 테넌트 컨텍스트 문제 해결을 함께 확인합니다.
증상 색인
| 증상 | 절 |
|---|---|
부트스트랩에서 could not translate host name "postgres" | Pest가 데이터베이스에 닿지 못한다 |
TenantCreated 안에서 관계가 null | 테넌트 라이프사이클 이벤트가 생성 중에 발생한다 |
| 부모와 자식 테스트 바인딩이 충돌 | uses in이 재귀적으로 매칭한다 |
| 합성 user resolver가 무시됨 | auth가 합성 user resolver를 덮어쓴다 |
| 테스트가 낡은 스키마로 돈다 | 템플릿 데이터베이스가 재생성되지 않았다 |
| 패키지 테스트가 발견되지 않음 | 패키지 테스트가 발견되지 않는다 |
단독으로는 통과하는데 --parallel에서 실패 | 워커 간 공유 상태 |
Pest가 데이터베이스에 닿지 못한다
원인. 호스트 셸의 ./vendor/bin/pest는 docker compose 네트워크 밖 호스트 PHP를 씁니다. 어떤 단정보다 앞선 부트스트랩에서 실패합니다.
해결.
task test -- packages/people/tests/Feature/PeoplePublicIntegrationContractTest.php --compact확인. 테스트가 실행되고 단정을 보고합니다.
예방. DB_HOST=127.0.0.1을 덮어쓰지 마세요. override는 그 명령 하나에만 적용되고 새어나가지 않지만, 테스트 데이터베이스가 아닌 엔드포인트로 스위트를 겨눕니다. .env.testing은 postgres compose 서비스를 지목합니다.
테넌트 라이프사이클 이벤트가 생성 중에 발생한다
A relation is null inside TenantCreated.
원인. Tenant::create()가 행이 아직 만들어지는 중에 TenantCreated를 발생시킵니다. 그 이벤트 안에서 접근한 관계는 관련 행이 아직 없어서 null로 캐싱됩니다.
해결. CreateTenantAction이 데이터베이스 설정을 끝낸 뒤에 도는 단계로 관계 접근을 미루세요. Tenant::create() 중에 ready 플래그를 설정하지 마세요. CreateTenantAction이 설정 후에 토글하게 두지 않으면 생성 파이프라인이 깨집니다.
확인. 이후 단계에서 관계가 해석됩니다.
예방. TenantCreated를 "테넌트가 준비됐다"가 아니라 "행이 존재한다"로 취급하세요.
uses in이 재귀적으로 매칭한다
Parent and child Pest test bindings collide.
원인. Pest의 uses()->in()이 재귀적으로 순회하므로 부모 디렉터리 바인딩이 자식 디렉터리 바인딩과 충돌합니다.
해결. 테스트 디렉터리가 중첩될 때는 디렉터리 경로가 아니라 glob 패턴을 써서 바인딩이 한 수준에서 멈추게 하세요.
확인. 각 테스트 파일이 정확히 바인딩 하나를 받습니다.
예방. 패키지 스위트는 tests/Pest.php에서 uses(TestCase::class)->in(__DIR__)로 한 번 바인딩합니다. 중첩은 의도적으로 추가하세요.
auth가 합성 user resolver를 덮어쓴다
The resolved request user is not the synthetic user assigned by the test.
원인. 테스트가 app()->instance('request', $request)로 합성 요청을 바인딩할 때, Laravel의 auth 통합이 요청 상태를 다시 붙이면서 앞선 setUserResolver()를 덮어쓸 수 있습니다.
해결. 순서가 중요합니다.
- 요청을 컨테이너에 바인딩합니다.
- 그다음
$request->setUserResolver(...)를 호출합니다.
resolver가 auth가 컨테이너를 다시 읽은 뒤에 붙어야 합니다.
확인. 요청 전체에서 해석된 사용자가 합성 사용자입니다.
예방. 두 호출을 인접하게, 그 순서로 유지하세요.
템플릿 데이터베이스가 재생성되지 않았다
Tests run against a stale schema after a schema change.
원인. 부트스트랩이 테스트별 migrate:fresh를 PostgreSQL 템플릿 복제로 대체합니다. 마이그레이션 파일 이름과 내용, 그리고 테넌트 스키마 덤프에 대한 해시마다 한 번 {DB_DATABASE}_tpl_c_{hash}(central)와 _tpl_t_{hash}(테넌트 체인)를 만들고, 모든 테스트가 거기서 복제합니다.
해시는 그 입력만 덮습니다. 마이그레이션 파일 밖에서 만들어진 스키마 변경 — 환경 의존 마이그레이션 분기, 템플릿 데이터베이스 직접 편집 — 은 재생성을 유발하지 않고, 테스트가 낡은 스키마로 돕니다.
해결. 테스트에 보이는 모든 스키마 변경을 마이그레이션 파일 편집으로 표현하세요. 해시가 바뀌면 다음 실행에서 두 템플릿이 재생성됩니다. 강제로 재생성하려면:
다른 테스트 실행을 중단한 뒤 공유 테스트 템플릿을 삭제합니다.
docker compose exec postgres psql -U postgres -c "SELECT 'DROP DATABASE ' || quote_ident(datname) || ';' FROM pg_database WHERE datname LIKE 'nexia_test%\_tpl\_%'" -t -A | docker compose exec -T postgres psql -U postgres확인. 다음 실행이 템플릿을 재생성하고 새 컬럼이나 제약이 보입니다.
예방. 마이그레이션 파일 밖에서 스키마를 바꾸지 마세요. 가장 헷갈리는 증상을 만드는 함정입니다. 파일에서 읽을 수 있는 마이그레이션이 데이터베이스에는 없는 상태가 됩니다.
패키지 테스트가 발견되지 않는다
The package test path is absent from a full task test run.
원인. 러너는 Composer 패키지가 루트 프로젝트에 요구사항으로 있고 설치된 패키지 테스트 경로만 발견합니다.
해결. 셋을 확인하세요.
| 확인 | 위치 |
|---|---|
extra.nexia.test_paths: ["tests"] | 패키지 composer.json |
autoload-dev 네임스페이스 선언 | 패키지 composer.json |
| 패키지가 요구사항이고 설치됨 | 활성화한 뒤 task rebuild |
확인. task test가 패키지 테스트를 포함하고, task test -- packages/people/tests가 그것만 돌립니다.
예방. 활성화가 패키지를 루트 요구사항으로 만듭니다.
워커 간 공유 상태
A test passes alone but fails with --parallel.
원인. Paratest 워커는 PHP 프로세스·central 데이터베이스·테넌트 접두가 각각 다릅니다. Static 속성과 singleton은 같은 워커의 테스트 사이에 남을 수 있지만 워커 간 공유 상태는 아닙니다. 병렬 실행에서만 실패한다면 공용 파일·cache key·외부 자원의 충돌도 확인해야 합니다.
해결. 오류가 가리키는 공유 자원을 찾습니다. 파일과 외부 key를 워커별로 분리하고 사용 후 정리하세요. 프로세스 안에서 바꾼 registry는 테스트마다 복원합니다. SDK Approval composer registry는 이를 위한 resetForTests()를 제공합니다.
확인. 테스트가 단독과 --parallel 모두에서 통과합니다.
예방. 테스트가 변경한 전역 상태와 registry를 복원합니다. App 테스트하기에서 필요한 범위를 선택하고, worker 의존 실패를 조사할 때 병렬 실행으로 넓힙니다.
관련 문서
- App 테스트하기 — 무엇을 테스트하고 어떻게 돌리는가
- 테넌트 컨텍스트 문제 해결 — 원인이 컨텍스트나 Grant일 때
- 데이터 모델과 마이그레이션 — 스키마 변경이 마이그레이션 파일에 속하는 이유