테스팅
PHPUnit + CI4 테스트 도구(CIUnitTestCase, DatabaseTestTrait)로 단위/통합 테스트를 작성하는 방법을 학습합니다.
CI4 테스팅 기초
CodeIgniter 4는 PHPUnit 기반의 테스트 도구를 내장하고 있습니다. CIUnitTestCase를 상속하면 CI4 서비스, 헬퍼, DB에 손쉽게 접근할 수 있습니다.
개별 함수나 메서드를 독립적으로 검증합니다. 외부 의존성(DB, 네트워크) 없이 빠르게 실행됩니다. CIUnitTestCase를 상속해 헬퍼·서비스·유틸을 테스트합니다.
실제 DB와 연동하여 비즈니스 로직 전체를 검증합니다. DatabaseTestTrait를 사용하면 테스트마다 DB를 자동으로 초기화합니다.
<?php
namespace Tests\App;
use CodeIgniter\Test\CIUnitTestCase;
final class MyTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
// 각 테스트 전에 실행
}
public function testSomething(): void
{
$this->assertSame('expected', someFunction());
}
}
DatabaseTestTrait
DB가 필요한 테스트에는 DatabaseTestTrait를 사용합니다. 테스트용 SQLite DB에 마이그레이션을 자동 실행하고, 각 테스트 후 롤백합니다.
use CodeIgniter\Test\DatabaseTestTrait;
final class PostServiceTest extends CIUnitTestCase
{
use DatabaseTestTrait;
// 테스트 DB에 마이그레이션 자동 실행
protected $migrate = true;
// 마이그레이션 파일 경로
protected $basePath = 'tests/_support/Database';
protected function setUp(): void
{
parent::setUp();
// skipValidation(true) 으로 시드 데이터 직접 삽입
(new PostModel())->skipValidation(true)->insertBatch([
['title' => '게시물 A', 'content' => '내용 A ...', 'views' => 100],
]);
}
}
tests/_support/Database/Migrations/ 에 마이그레이션을 두면 운영 DB와 완전히 분리됩니다.
테스트 실행 명령어
./vendor/bin/phpunit --testdox
./vendor/bin/phpunit tests/app/Helpers/PlaygroundHelperTest.php --testdox
./vendor/bin/phpunit tests/app/Services/PostServiceTest.php --testdox
<testsuites>
<testsuite name="App">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
PlaygroundHelperTest — 단위 테스트
커스텀 헬퍼 함수 5개(format_filesize, time_ago, truncate_text, highlight_keyword, korean_number)에 대한 13개의 단위 테스트입니다.
final class PlaygroundHelperTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
helper('playground'); // 헬퍼 로드
}
// format_filesize: 바이트 → 사람이 읽기 쉬운 크기 문자열
public function testFormatFilesizeBytes(): void
{
$this->assertSame('512 B', format_filesize(512));
}
public function testFormatFilesizeKilobytes(): void
{
$this->assertSame('1.5 KB', format_filesize(1536));
}
public function testFormatFilesizeMegabytes(): void
{
$result = format_filesize(2097152);
$this->assertStringContainsString('MB', $result);
$this->assertStringContainsString('2', $result);
}
// truncate_text: 지정 길이 초과 시 말줄임표
public function testTruncateTextShortString(): void
{
$this->assertSame('짧은글', truncate_text('짧은글', 20));
}
public function testTruncateTextLongString(): void
{
$result = truncate_text('가나다라마바사아자차카타파하가나다라마바사', 10);
$this->assertStringEndsWith('...', $result);
$this->assertLessThanOrEqual(13, mb_strlen($result));
}
// time_ago: 타임스탬프 → "N분 전" 형식
public function testTimeAgoSeconds(): void
{
$this->assertStringContainsString('초 전', time_ago(time() - 30));
}
// korean_number: 숫자 → 만/억 단위 포맷
public function testKoreanNumberMan(): void
{
$this->assertStringContainsString('만', korean_number(50000));
}
public function testKoreanNumberEok(): void
{
$this->assertStringContainsString('억', korean_number(100000000));
}
}
PostServiceTest — 통합 테스트
DatabaseTestTrait를 사용해 실제 SQLite 테스트 DB 위에서 서비스 레이어의 동작을 검증합니다. 7개의 테스트로 구성됩니다.
final class PostServiceTest extends CIUnitTestCase
{
use DatabaseTestTrait;
protected $migrate = true;
protected $basePath = 'tests/_support/Database';
private PostService $service;
protected function setUp(): void
{
parent::setUp();
$this->service = new PostService(new PostModel());
// 테스트용 시드 데이터 직접 삽입
(new PostModel())->skipValidation(true)->insertBatch([
['title' => '게시물 A', 'content' => '테스트 내용입니다 A', 'views' => 100],
['title' => '게시물 B', 'content' => '테스트 내용입니다 B', 'views' => 50],
['title' => '게시물 C', 'content' => '테스트 내용입니다 C', 'views' => 200],
]);
}
// getTopPosts: 조회수 내림차순 정렬 확인
public function testGetTopPostsOrderedByViews(): void
{
$posts = $this->service->getTopPosts(5);
if (count($posts) >= 2) {
$this->assertGreaterThanOrEqual($posts[1]->views, $posts[0]->views);
}
$this->assertTrue(true);
}
// getSummary: 필수 키 존재 여부 확인
public function testGetSummaryReturnsRequiredKeys(): void
{
$summary = $this->service->getSummary();
$this->assertArrayHasKey('total', $summary);
$this->assertArrayHasKey('total_views', $summary);
$this->assertArrayHasKey('avg_views', $summary);
}
// search: 빈 키워드 → 빈 배열 반환
public function testSearchReturnsEmptyForBlankKeyword(): void
{
$this->assertEmpty($this->service->search(''));
}
// create: 제목 없이 생성 시도 → 실패
public function testCreateFailsWithMissingTitle(): void
{
$result = $this->service->create(['content' => '본문만 있음']);
$this->assertFalse($result['success']);
$this->assertArrayHasKey('errors', $result);
}
// create: 정상 데이터 → 성공 + id 반환
public function testCreateSucceedsWithValidData(): void
{
$result = $this->service->create([
'title' => '테스트 게시물',
'content' => '서비스 레이어를 통해 저장한 테스트 본문입니다.',
'author' => '테스트작성자',
]);
$this->assertTrue($result['success']);
$this->assertGreaterThan(0, $result['id']);
}
}
주요 주의사항
Services::validation()은 싱글톤을 반환해 이전 테스트의 에러가 남아 있습니다. Services::validation(null, false)로 항상 새 인스턴스를 생성해야 합니다.
$basePath = 'tests/_support/Database'를 지정하고 해당 경로에 테스트 전용 마이그레이션 파일을 두어야 합니다. 운영 마이그레이션과 분리하세요.
테스트 실행하기
실행할 테스트 스위트를 선택하고 PHPUnit을 실행합니다.