홈으로 돌아가기

PHPUnit covers: 개발자를 위한 탐색 및 테스트 커버리지

이 기사는 PHPUnit에서 covers annotations와 attributes를 사용하여 테스트와 애플리케이션 코드 간 탐색을 개선하는 방법을 설명합니다. 테스트를 메서드에 연결하는 접근 방식, IDE 설정, 커버리지 제한 등을 다룹니다.

PHP 테스트 개선: PHPUnit covers 가이드
Advertisement 728x90

PHPUnit: @covers로 테스트 탐색과 커버리지 향상하기

PHP 웹 컨트롤러 테스트 시 테스트와 앱 코드 간 탐색이 골치 아픈 문제로 이어지곤 합니다. PHPUnit의 @covers 어노테이션과 속성이 이를 어떻게 해결하며, 커버리지를 희생하지 않고 개발을 간소화하는지 알아보겠습니다.

테스트와 코드 간 탐색의 어려움

HTTP 요청으로 컨트롤러를 테스트할 때 메서드를 직접 호출할 수 없어 테스트에서 코드로 바로 이동하기 어렵습니다. 예를 들어 /hello 엔드포인트 테스트는 GreetingController::hello()를 직접 참조하지 않습니다. 이로 인해 IDE에서 수동 검색을 해야 해 속도가 느려집니다.

다음은 컨트롤러와 테스트 예시입니다:

Google AdInline article slot
final class GreetingController extends AbstractController
{  
    #[Route('/hello')]
    public function hello(): Response
    {
        return new Response($this->generate("Hello!"));
    }
}
class GreetingControllerTest extends WebTestCase
{
    public function test_hello(): void
    {
        $content = get('/hello');
        assert_starts_with('Hello!', $content);
    }
}

테스트와 코드를 연결하는 3가지 방법

개발자들은 탐색을 개선하기 위해 여러 방법을 씁니다.

1. 리플렉션으로 네이티브 구문 사용

문자열 경로 대신 리플렉션으로 메서드 호출처럼 사용: request(GreetingController::hello(...)). 단점: 테스트가 앱 코드에 의존해 변경 시 깨질 위험이 있습니다.

Google AdInline article slot

2. @see 어노테이션

PHPDoc으로 링크:

/**
 * @see \App\Controller\GreetingController::hello()
 */
public function test_hello(): void { ... }

장점: IDE 지원. 단점: 전체 클래스 경로 필요, 일반 태그라 특별 처리 없음.

Google AdInline article slot

3. PHPUnit 전용 Covers

커버리지와 탐색을 위해 만들어진 어노테이션 또는 속성.

PHPUnit에서 Covers 어노테이션 사용하기

PHPUnit 12 이전에는 @coversDefaultClass@covers가 주를 이뤘습니다:

/**
 * @coversDefaultClass \App\Controller\GreetingController
 */
class GreetingControllerTest extends WebTestCase
{
    /**
     * @covers ::hello
     */
    public function test_hello(): void { ... }
}

장점:

  • PhpStorm 최고 수준 지원: 자동완성, 탐색, 사용 감지.
  • 테스트 생성용 커스텀 템플릿.

PhpStorm 템플릿 단점:

  • PHPUnit 템플릿 2개로 제한.
  • 이름이 헷갈림.
  • 생성기 가끔 버그.

PHPUnit 12+에서 Covers 속성으로 전환

PHPUnit 12는 어노테이션을 속성으로 바꿨지만, 클래스에만 적용되고 메서드에는 안 됩니다.

기본 사용:

#[CoversClass('App\Controller\GreetingController')]
class GreetingControllerTest extends WebTestCase
{
    public function test_hello(): void { ... }
}

메서드 세부 지정 없어 탐색 정밀도가 떨어집니다.

CoversMethod로 메서드 지정:

#[CoversMethod('App\Controller\GreetingController', 'hello')]
#[CoversMethod('App\Controller\GreetingController', 'bye')]
class GreetingControllerTest extends WebTestCase
{
    public function test_hello(): void { ... }
    public function test_bye(): void { ... }
}

단점: 여러 메서드 테스트 시 속성 과다.

최적 전략: 메서드당 전용 테스트 클래스

메서드별 테스트 클래스 분리:

#[CoversMethod('App\Controller\GreetingController', 'hello')]
class HelloTest extends WebTestCase
{
    public function test_hello(): void { ... }
    public function test_hello2(): void { ... }
}

이점:

  • 정신적 부하 적음.
  • 메서드별 covers 불필요.
  • 깔끔한 설계 촉진: 컨트롤러당 엔드포인트 하나.

속성 템플릿은 어노테이션처럼 동작하나 생성 후 CoversMethod 수동 조정 필요.

IDE 통합과 향후 개선

PhpStorm은 이미 Ctrl+Shift+T로 테스트-클래스 이동 지원. 메서드 수준 탐색 곧 추가될 전망.

핵심 요약

  • 탐색 향상을 위해 covers 사용, 커버리지 영향 주의.
  • PHPUnit 12+ 속성은 클래스 전용이니 테스트 방식 조정.
  • 유지보수 쉬운 메서드당 테스트 클래스 하나.
  • IDE 템플릿으로 자동 covers 설정.
  • PhpStorm 속성 지원 개선 업데이트 주시.

커버리지 제한: Covers의 최대 단점

Covers의 주 역할은 클래스/메서드 범위 커버리지 제한. CoversClass는 외부 코드 보고서 제외—엄격 프로젝트 좋지만, 대부분 개발자는 누락만 찾고 싶어합니다.

비활성화 우회 방법:

  • 과도한 해킹: 테스트 전 태그 제거 스크립트.
  • 실용적 해결: Composer로 phpunit/php-code-coverage 패치.

프로젝트 필요와 팀 선호에 맞게 선택.

— Editorial Team

Advertisement 728x90

다음 읽기