Laravel Test 에 관하여..
보통 in-memory (sqlite) 디비와 같이 사용하길 권장하는것 같다. RDBMS 에 비해 상대적으로 속도면에서도 빠르고 초기 데이터베이스 셋팅을 할 필요가 없으니 편할수 있다. 근데 개인적으론 테스트디비로 sqlite을 사용했을때 이슈가 좀 있었다.
내가 경험했던 이슈는 전체 스키마에 걸쳐 인덱스명이 유니크 하지 않아 인덱스명 duplicate 오류가 발생했었다. 그리고 sqlite 에서 index명 length limit 관련해서도 에러가 났었던거 같은데 기억이 가물가물 하다
그리고 현재 재직중인 회사에서도 sqlite 를 쓰지 않고 운영 환경과 같은 mysql 을 테스트디비로 사용하고 있다.
왜냐하면 좌표(geo) 컬럼은 sqlite 에서 지원해주지 않는다고 한다.
그래서 개인적으론 테스트 디비 또한 운영 환경과 최대한 똑같이 맞춰서 하는게 더 좋다고 생각한다. (최소한 위에 언급된 이슈는 피할수있을것 이다.)
근데 최근 또 다른 이슈 아닌 이슈를 겪고 있는데 테스트코드를 다들 너무 열심히 짰는지 테스트 케이스 수가 꽤 많다. 그래서 전체 테스트 실행시 시간이 되게 오래 걸린다..그래서 기회가 되면 이 부분 최적화 작업을 해보고싶다. 우선 테스트코드 안에서 개선할 부분이 있는지 살펴보고 그 이후 병렬처리를 해야하지 않을까싶다.
우선 라라벨(laravel9)에서 제공해주는 테스트 디비를 위한 3가지 유용한 traits에 대해 알아보도록 하자.
1. DatabaseMigrations (Illuminate\Foundation\Testing\DatabaseMigrations.php)
Testcase 실행시 DB 마이그레이션 실행
$this->artisan('migrate:fresh', $this->migrateFreshUsing());
$this->app[Kernel::class]->setArtisan(null);
종료 전엔 마이그레이션 롤백
$this->beforeApplicationDestroyed(function () {
$this->artisan('migrate:rollback');
RefreshDatabaseState::$migrated = false;
});
테스트 시작시 DB 에 있는 모든 테이블을 drop 해버리고 다시 migrate 커맨드를 실행한다. 그리고 테스트 종료전 migrate:rollback 을 하여 다시 모든 테이블을 날려버린다.
2. DatabaseTransactions (Illuminate\Foundation\Testing\DatabaseTransactions.php)
Testcase 실행시 DB 트랜잭션 시작
$database = $this->app->make('db');
foreach ($this->connectionsToTransact() as $name) {
$connection = $database->connection($name);
$dispatcher = $connection->getEventDispatcher();
$connection->unsetEventDispatcher();
$connection->beginTransaction();
$connection->setEventDispatcher($dispatcher);
if ($this->app->resolved('db.transactions')) {
$this->app->make('db.transactions')->callbacksShouldIgnore(
$this->app->make('db.transactions')->getTransactions()->first()
);
}
}
종료 전엔 DB 트랜잭션 롤백
$this->beforeApplicationDestroyed(function () use ($database) {
foreach ($this->connectionsToTransact() as $name) {
$connection = $database->connection($name);
$dispatcher = $connection->getEventDispatcher();
$connection->unsetEventDispatcher();
$connection->rollBack();
$connection->setEventDispatcher($dispatcher);
$connection->disconnect();
}
});
DatabaseMigrations 과의 차이점은 DatabaseMigrations 의 경우 테스트 이전, 이후 DB 전체를 모두 초기화 하는 것이고
DatabaseTransactions 은 테스트 실행시 DB에 존재하는 테이블들을 기준으로 트랜잭션 안에서만 테스트를 하는 것이다.
그래서 테스트 실행시 DB 에 테이블 마이그레이션이 필요하다.
그리고 가장 큰 차이점으론 실행 속도의 차이 일 것이다. DB 전체를 초기화 하는 작업은 꽤 비싼 비용이다. 그래서 맨 처음에 한 번만 DB 테이블에 대한 마이그레이션이 필요하고 그 이후엔 모두 트랜잭션으로 처리하는게 속도 측면에서 우월할 것이다.
3. RefreshDatabase (Illuminate\Foundation\Testing\RefreshDatabase.php)
기본적으로 2. DatabaseTransactions 과 작동 방식은 똑같지만 방금 위에서 언급했던 내용에 대한 로직이 추가되어 있다.
DB 전체를 초기화 하는 작업은 꽤 비싼 비용이다. 그래서 맨 처음에 한 번만 DB 테이블에 대한 마이그레이션이 필요하고 그 이후엔 모두 트랜잭션으로 처리하는게 속도 측면에서 우월할 것이다.
if (! RefreshDatabaseState::$migrated) {
$this->artisan('migrate:fresh', $this->migrateFreshUsing());
$this->app[Kernel::class]->setArtisan(null);
RefreshDatabaseState::$migrated = true;
}
$this->beginDatabaseTransaction();
RefreshDatabaseState::$migrated 는 static 으로 선언되어 있어 Test 실행 내내 모든 테스트 케이스에서 공유될 것이고 이는 맨처음에 한 번만 실행되고 그 이후엔 DatabaseTransactions 와 똑같이 작동한다.
참고자료
- https://adamwathan.me/2016/11/14/a-better-database-testing-workflow-in-laravel/
- https://gist.github.com/adamwathan/dd46a8501097942a771925c02bac0111
'IT > 라라벨' 카테고리의 다른 글
[laravel] 다대다 관계 pivot 테이블 컬럼 업데이트 (0) | 2023.03.05 |
---|---|
[laravel] Model $attributes 필드 접근시 주의점 (0) | 2022.12.28 |
[laravel] SPA 웹 어플리케이션에서 csrf_token 을 사용할때 발생할수 있는 이슈 CSRF token mismatch (0) | 2022.12.22 |
[라라벨] laravel 설치 명령어 에러 해결방법 (0) | 2020.11.14 |
[php] xml 포맷 데이터 배열로 바꾸기 (0) | 2020.08.18 |