PHPUnit Annotation 정리
서론
PHP Unit을 사용하면서도 잘 모르는 어노테이션을 PHPUnit v8.4 기준(2019-11-19 최신버전)으로 정리하였습니다.
@author
테스트를 작성자별 그룹화 필터링 할 때
@group
어노테이션의 별칭으로, 테스트를 작성자별로 그룹화하여 필터링 하는데 사용할 수 있습니다.
@after
각 테스트가 끝난 뒤 실행 하려 할 때
각 테스트 메소드들이 실행 된 후, 특정 메소드를 실행하고자 할때 사용할 수 있습니다.
각 테스트가 끝난 뒤 트랜잭션 커밋or롤백을 한다거나, 생성된 파일을 삭제하는 등의 처리를 하는데 사용하면 좋을 것 같습니다.
예제 코드
1 |
|
@afterClass
모든 테스트가 끝난 후 실행 하려 할 때
모든 테스트가 끝난 후, 공유된 자원들을 정리하기 위해 호출할 정적 메소드를 지정 할 수 있습니다.
해당 부분에서 테스트 실행시 만든 DB 커넥션을 회수하거나, 전체 트랜잭션을 처리 하거나, 소켓을 닫는 등의 처리를 할 수 있을것 같습니다.
예제 코드
1 |
|
@backupGlobals
글로벌 변수를 유지하고 싶다면
모든 글로벌 변수를 각 테스트 전에 백업하고, 각 테스트 이후 해당 백업을 복원시킵니다.
메소드 레벨에서 재정의가 가능합니다.
해당 설명만으로는 이해가 잘 되지 않아서 직접 예제 코드를 만들어 보았습니다.
클래스 스코프 밖에 정의된 글로벌 변수인 $className
을 @backupGlobals
어노테이션이 enabled 되어 있는 테스트 코드에서는 실행이전 값을 백업하여두고 테스트가 끝나면 복원이 되어,
두번째 테스트코드에서도 “MyTest”라는 값을 가지고 있게됩니다.
예제 코드
1 |
|
@backupStaticAttributes
정적 속성을 사용하려 할 때
선언된 클래스들 안의 모든 정적 속성을 각 테스트 전에 백업하고, 각 테스트 후에 해당 백업을 복원 시킵니다.
클래스 레벨에도 선언 가능하며, 각 테스트 메소드에서 추가 제어 가능합니다.
예제 코드
**아래 코드는 정상 동작 안 합니다.**1 |
|
@before
각 테스트 실행전에 실행 하려 할 때
각 테스트 메소드가 호출되기 전에 실행할 메소드를 지정할 수 있습니다.
아래와 같이 beforeMethod는 각 메소드 호출전에 실행되지만,
users 배열의 값이 증가되지는 않습니다.
예제 코드
1 |
|
@beforeClass
테스트 실행전 공유 속성을 만들 때
해당 클래스에서 테스트가 실행되기전 공유 하기 위한 정보를 설정하기 위해 호출 할 static 메소드에 지정하여 사용할 수 있습니다.
예제 코드
1 |
|
@codeCoverageIgnore*
코드 커버리지 분석시 제외할 라인에 사용할 수 있습니다.
예제 코드
1 |
|
@covers
테스트 영역을 명시하려 할 때
어떤 영역을 테스트 하고자 하는지 명시하고자 할 때 사용합니다.
이와 같이 명시 하면 IDE(PHPStorm)에서 연결되어 있어 ctrl+shift+T 를 이용해 테스트로 바로 이동이 가능해지고, usage로 찾을 수 있어 메소드명 수정시 같이 반영됩니다.
예제 코드
1 | /** |
@coversDefaultClass
너무 긴 네임스페이스와 클래스명을 반복해서 쓰고 싶지 않을 때
기본 네임스페이스나 클래스명을 명시하는데 사용할 수 있어, @covers
어노테이션에 긴 네임스페이스나, 클래스명을 반복해서 사용할 필요가 없어집니다.
해당 어노테이션에는 정규화 된 클래스명을 사용해야하기때문에,
모호하지 않도록 클래스명 맨 앞에 \
로 시작하는것을 추천합니다.
아래 예제 코드와 같이 @covers \Foo\CoveredClass::publicMethod
를 @covers ::publicMethod
로 줄여 쓸 수 있는 이점을 얻게 됩니다.
예제 코드
1 |
|
@coversNothing
작성예정
클래스나 메소드레벨에서 사용할 수 있고 @covers
어노테이션을 덮어 씁니다.
@DataProvider
메소드를 이용해 파라미터를 주입하고 싶을때
@dataProvider
를 사용하면 메소드의 파라미터로 전달할 수 있습니다.
Java Junit 패키지에서 JunitParams를 이용하여 @Parameters
어노테이션을 사용하는것과 동일한 효과를 얻을 수 있습니다.
- 예제 코드
- 아래 예제 코드와 같은 테스트는 배열의 각 값 들이
$a
,$b
,$expected
로 바인딩 되며,
총 4개의 배열이 자동 주입되어 테스트가 4회 수행됩니다.- ```php assertSame($expected, $a + $b);
}
public function additionProvider()
{
return [
[0, 0, 0],
[0, 1, 1],
[1, 0, 1],
[1, 1, 3]
];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- 아래와 같이 이름이 정의된 dataset을 사용할 수도 있습니다.
- ```php
<?php
use PHPUnit\Framework\TestCase;
class DataTest extends TestCase
{
/**
* @dataProvider additionProvider
*/
public function testAdd($a, $b, $expected)
{
$this->assertSame($expected, $a + $b);
}
public function additionProvider()
{
return [
'adding zeros' => [0, 0, 0],
'zero plus one' => [0, 1, 1],
'one plus zero' => [1, 0, 1],
'one plus one' => [1, 1, 3]
];
}
}
- ```php assertSame($expected, $a + $b);
}
public function additionProvider()
{
return [
[0, 0, 0],
[0, 1, 1],
[1, 0, 1],
[1, 1, 3]
];
}
}
- 아래 예제 코드와 같은 테스트는 배열의 각 값 들이
@depends
테스트 코드간의 종속성 정의
@depends
어노테이션 사용시 테스트 코드간의 종속성을 선언 할 수 있습니다.
실행순서를 정의하는것은 아니지만, @depends
에 정의된 테스트의 리턴값의 레퍼런스를 전달합니다.
레퍼런스 전달이 아닌 값의 깊은 복사를 원할 경우 @depends clone
를 이용하고,
PHP에서 clone으로 불리는 얕은 복사를 원할 경우 @depends shallowClone
를 이용하면 됩니다.
@doesNotPerformAssertions
값에 대한 assertion 없이 테스트 코드를 실행만 하고자 할때
아래와 같이 테스트를 수행하지 않을 경우 This test did not perform any assertions
와 같은 Warning이 발생됩니다.
해당 어노테이션을 사용하면 Risky 없이 OK (1 test, 0 assertions)
로 성공 처리됩니다.
예제 코드
1 |
|
@group
테스트 코드에 태그를 달고 싶을때
@group
어노테이션을 이용하여, 테스트 코드에 1개 이상의 태그와 같이 묶음 필터를 추가할 수 있습니다.
XML 설정 파일 주입을 이용 하거나 CLI에서 실행시 --group
과 --exclude-group
를 이용해서 테스트 실행 대상 또는 제외그룹을 설정할 수 있습니다.
@large
60초 이상 실행 되면 실패 처리 하고자 할 때
@group large
의 별칭으로,PHP_Invoker
패키지가 설치되어 있고, strict mode가 실행되어 있으면 60초 이상 실행 될 경우 실패 처리됩니다.
해당 타임아웃에 관한 정보는 설정 정보 XML의 timeoutForLargeTests
속성을 통해 설정 할 수 있습니다.
@medium
10초 이상 실행 되면 실패 처리 하고자 할 때
@group medium
의 별칭으로, PHP_Invoker
패키지가 설치되어 있고, strict mode가 실행되어 있으면 10초 이상 실행 될 경우 실패 처리됩니다.
해당 타임아웃에 관한 정보는 설정 정보 XML의 timeoutForMediumTests
속성을 통해 설정 할 수 있습니다.
Medium 테스트는 @large
테스트에 의존적이여서는 안됩니다.
@preserveGlobalState
테스트가 별도의 프로세스에서 실행될때 직렬화 오류 방지
테스트가 별도의 프로세스에서 실행될 때, PHPUnit은부모 프로세스에서 글로벌 state를 직렬화 한 값을 자식 프로세스에서 역직렬화하여 상태를 보존합니다.
부모 프로세스에서 직렬화 할 수 없는 글로벌 state가 있는 경우, 해당 옵션을 disable
처리하여 방지할 수 있습니다.
@requires
특정 조건일때만 테스트를 수행하고자 할 때
PHP의 버전이나 extensions 설치여부 등 전제 조건을 체크하여 테스트를 건너뛸 수 있습니다.
<
, <=
, >
, >=
, =
, ==
, !=
, <>
등의 비교 연산자를 사용하여 버전을 비교할 수 있습니다.
해당 어노테이션을 이용해 체크 가능한 항목은 아래와 같습니다.
- 체크 가능한 조건
- PHP
- PHP 버전
- PHPUnit
- PHP Unit 버전
- OS
PHP_OS
상수와 정규식으로 매칭되는 값입니다.
ex) WIN32|WINNT
- OSFAMILY
PHP_OS_FAMILY
상수와 매칭되는 값으로 PHP 7.2.0부터 사용가능합니다.
ex) Windows
- function
- 함수 존재 여부 → function_exists()
- extension
- extension 설치 여부 및 버전 체크
- PHP
예제 코드
1 |
|
@runTestsInSeparateProcesses
테스트 클래스 내의 모든 테스트 메소드가 별도 PHP프로세스에서 테스트코드를 실행 하는것을 명시할 때
해당 테스트 클래스 내의 모든 테스트 메소드들이 별도의 PHP 프로세스에서 실행되어야 함을 표시 할 때 사용합니다.
PHPUnit은 직렬화를 통해 Global state를 유지하려 하기 때문에, 직렬화가 불가능한 부분은 @preserveGlobalState
를 참조하세요.
@runInSeparateProcess
해당 테스트 메소드가 별도의 PHP 프로세스에서 실행되어야 함을 표시 할 때 사용합니다.
PHPUnit은 직렬화를 통해 Global state를 유지하려 하기 때문에, 직렬화가 불가능한 부분은 @preserveGlobalState
를 참조하세요.
@small
@group small
의 별칭으로, PHP_Invoker
패키지가 설치되어 있고, strict mode가 실행되어 있으면 1초 이상 실행 될 경우 실패 처리됩니다.
해당 타임아웃에 관한 정보는 설정 정보 XML의 timeoutForSmallTests
속성을 통해 설정 할 수 있습니다.
Medium 테스트는 @large
와 @medium
로 마킹된 테스트에 의존적이여서는 안됩니다.
※ 테스트의 실행 시간 제어를 하고자 할 때, @small
, @medium
, @large
와 같은 어노테이션을 명시적으로 사용해야합니다.
@test
테스트 메소드명을 test로 시작하고싶지 않을 때
테스트 메소드는 메소드명의 prefix로 test를 사용합니다.
테스트 메소드명의 prefix로 test
를 사용하지 않는 대안으로, 주석에 @test
어노테이션을 사용하면 테스트 메소드라고 인식됩니다.
@testdox
testdox 옵션으로 생성되는 문서의 설명을 대체하려 할 때
--testdox
를 옵션으로 주었을때, 메소드의 이름으로부터 만들어진 설명을 오버라이딩 할 수 있습니다.
클래스 또는 메소드의 설명을 더 명확히 만들어 agile document를 만들 수 있습니다.
주의할 점으로는 PHPUnit v7.0까지는 어노테이션 파싱 오류로, @test로 인식되어 동작합니다.
@testWith
주석을 이용해 파라미터를 주입하고 싶을때
@dataProvider
는 호출될 메소드를 필요로 하지만, 주석만을 이용해 테스트하고자 할 때에는@testWith
를 사용할 수 있습니다.
JSON 포맷은 연관배열로 주입됩니다.
주의 할 점은 여러개의 dataset을 정의할 때에는 라인당 하나씩 지정해야합니다.
아래의 두개의 코드는 동일하게 동작합니다.
예제 코드
1 |
|
@ticket
Ticket ID(JIRA 이슈 코드와 같은)로 테스트를 필터링 할 때
@group
어노테이션의 별칭. ticket ID를 이용하여 테스트를 필터링 할 수 있도록 하여줍니다.
@uses
테스트에 의해 실행될 코드를 지정합니다.
좋은 예제는 아래와 같이 유닛 테스트 코드에 필요한 Object 값 입니다.
예제 코드
1 | /** |
해당 어노테이션에는 정규화 된 클래스명을 사용해야하기때문에,
모호하지 않도록 클래스명 맨 앞에 \
로 시작하는것을 추천합니다.
PHPUnit Annotation 정리