티스토리 뷰
이번 글에서는 JUnit에서 제공하는 Tagging과 Filtering 기능에 대해 알아보려고 한다. 자바 어플리케이션은 빌드 시 기본적으로 모든 테스트가 수행되는데 이때 태깅 기능을 통해 빌드하는 특정 환경 별로 각기 다른 테스트가 수행되도록 설정할 수 있다. JUnit에서는 테스트 클래스와 테스트 메소드를 태깅하기 위해 @Tag
어노테이션을 제공하며 이를 기반으로 실행할 테스트들을 필터링한다.
Tag 문법 규칙
다음과 같이 @Tag
의 value 속성으로 태그 이름을 지정하는데, 특정한 규칙을 만족하는 문자열만을 태그 이름으로 사용할 수 있다.
class UnitTest {
@Tag("fast")
@Test
void fast() {
System.out.println("fast 테스트 실행");
}
@Tag("slow")
@Test
void slow() {
System.out.println("slow 테스트 실행");
}
}
- 태그는 null이거나 공백이어서는 안됨
- 태그 문자열 중간에 공백 포함 불가 (문자열 맨 앞이나 맨 뒤에 공백이 포함되는 경우에는 JUnit이 알아서 trim)
- 다음과 같은 예약어 포함 불가
, | 콤마 |
( | 왼쪽 소괄호 |
) | 오른쪽 소괄호 |
& | ampersand |
| | vertical bar |
! | 느낌표 |
Tag 지정 표현식
실행할 테스트들을 지정하거나 필터링하기 위해서 태그 이름과 Boolean Operator(&, |, !)을 사용한다. 뿐만 아니라 JUnit이 제공하는 2개의 표현식이 존재하는데 다음과 같다.
any() |
@Tag 어노테이션이 붙은 모든 테스트 실행 |
none() |
@Tag 어노테이션이 붙지 않은 모든 테스트 실행 |
예를 들어 "fast | slow" 표현식은 fast 또는 slow 태그가 있는 테스트만을 실행, "fast & !slow"는 fast 태그가 있으면서 slow 태그는 없는 테스트만을 실행하는 것을 의미한다. 참고로 하나의 테스트 클래스 또는 테스트 메소드에 대해 여러 개의 @Tag
어노테이션을 선언하는 것이 가능하다. 따라서 개발 시에는 @Tag("develop")
태그가 있는 테스트를, 배포 시에는 @Tag("product")
태그가 있는 테스트를 실행하도록 설정한 경우 개발 시에도, 배포 시에도 실행되길 원한다면 이 2개의 어노테이션을 모두 붙여주면 된다.
그럼 이제 Intellij IDE에서 태그를 기반으로 실행할 테스트를 지정하는 방법을 알아보자.
Run → Edit Configurations... → JUnit Configuration이 없다면 + 버튼의 Add New Configuration 메뉴에서 추가
생성된 JUnit Configuration의 Build and run 메뉴에서 Tags를 선택한 후 오른쪽에 원하는 태그 표현식을 작성한다. 현재 작성한 fast & !slow 표현식은 fast 태그는 포함하면서 slow 태그는 없는 테스트를 지정한 것이다. 이후 APPLY 버튼을 눌러 설정을 완료한다. (Name 값은 아무거나 상관없다.)
이제 오른쪽 상단의 드롭박스에서 실행할 Configurations로 앞서 생성한 Tags를 지정하고 Run 버튼을 눌러 결과를 확인해보자.
다음과 같이 UnitTest 클래스 내의 fast 태그가 있는 테스트만이 실행된 것을 확인할 수 있다.
다음으로 Gradle로 어플리케이션을 빌드하는 경우 build.gradle 파일에서 실행할 테스트를 지정하는 방법을 알아보자. 앞서 IDE를 통한 방법에서는 Configuration을 생성하고 특정 Configuration으로 Run할 경우에만 태그 기반 필터링이 가능했지만, 이 방법을 통해서는 애플리케이션을 빌드할 때나 전체 테스트를 실행할 때 등 테스트 필터링에 대한 전역적인 설정이 가능하다.
설정 방법은 더 간단한데 build.gradle 파일 끝에 있던 기존 test task에 다음과 같이 추가로 코드를 작성해주면 된다. includeTags
, excludeTags
각각에 실행할 테스트 태그와 실행하지 않을 테스트 태그를 작성한다. IDE 방법과 달리 조금 번거로운 점은 코끼리 버튼을 눌러 작성한 내용을 sync해주는 작업이 필요하다는 것이다. 이후 테스트 클래스, 메소드 별로나 ./gradlew clean test
명령어로 테스트할 때, 어플리케이션을 빌드할 때 모두 build.gradle 파일에서 지정한 테스트 태그 기반의 필터링이 적용된다.
tasks.named('test') {
useJUnitPlatform {
includeTags 'fast' //추가
excludeTags 'slow' //추가
}
}
빌드 툴로 Maven을 사용하는 경우에는 다음을 참고
Custom Tag Annotation 생성
@Tag
어노테이션을 통해 태깅하는 경우에는 태그 이름에 오타가 발생할 확률이 높다. 뿐만 아니라 태그 이름을 변경하려면 모든 @Tag
어노테이션을 찾아 일일이 수정해주어야 하는 번거로움이 있다. 이 문제점을 해결해주는 것이 있는데 바로 Custom Tag Annotation이다. 이는 기존의 @Tag("fast")
형태의 어노테이션 대신 @Fast
라는 어노테이션을 직접 정의하여 사용하는 것이다. 백문이불여일견 일단 코드를 살펴보자.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}
실제 @Tag
어노테이션 파일에 들어가보면 다음과 같이 정의가 되어있는데 이때 @Target
, @Retention
부분만 복붙한 후 Custom Annotation으로 만들고 싶은 @Tag("fast")
를 추가로 작성해주면 된다.
새로 정의한 어노테이션은 다음과 같이 사용한다. 어노테이션 이름에 오타가 나더라도 컴파일 오류로 쉽게 잡을 수 있으며 태그 이름은 하나의 어노테이션 파일에서 관리하므로 변경도 더욱 용이해진다.
class UnitTest {
@Fast
@Test
void fast() {
}
}
여기서 한 단계 더 나아가 위 코드에서 사용된 2개의 어노테이션(@Fast
, @Test
)을 1개로 줄일 수도 있다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
@Test
public @interface FastTest {
}
기존 어노테이션 정의 부분에 @Test
어노테이션을 추가해주면 되는데, 이렇게 새로 정의된 어노테이션으로 다음과 같이 더 간단하게 테스트 코드를 작성할 수 있다.
class UnitTest {
@FastTest
void fast() {
}
}
빌드 환경별 테스트 설정
태그를 통해 빌드하는 환경이 개발 환경인지 배포 환경인지 등에 따라 실행되는 테스트를 다르게 할 수도 있다. 이것도 마찬가지로 build.gradle 파일에 작성하면 되는데, if문을 통해 해당 System Environment Variable 값이 무엇인지에 따라 실행할 테스트의 태그를 지정할 수 있다. 시스템 환경 변수는 gradle에서 System.getenv("변수 이름")
으로 접근 가능하다. 먼저 작성한 테스트 코드는 다음과 같다.
class UnitTest {
@Tag("develop")
@Test
void develop() {
System.out.println("develop 테스트");
fail(); //test fail 오류 발생!
}
@Tag("product")
@Test
void product() {
System.out.println("product 테스트");
}
}
다음은 build.gradle 파일에 추가로 작성한 코드로, 이름이 BUILD인 환경 변수값이 develop인 경우에는 develop 태그가 있는 테스트만, product인 경우에는 product 태그가 있는 테스트만이 실행되며 해당 환경 변수값이 없는 경우에는 기본적으로 모든 테스트가 실행된다.
tasks.named('test') {
useJUnitPlatform {
if (System.getenv('BUILD') == 'develop') {
includeTags 'develop'
}
if (System.getenv('BUILD') == 'product') {
includeTags 'product'
}
}
}
참고로 시스템 환경 변수는 어플리케이션이 실행되는 OS 환경에 설정되는 것으로 해당 어플리케이션 뿐만 아니라 현재 시스템에서 실행되고 있는 어떤 프로세스든 접근이 가능하다. 따라서 gradle 파일 내부에서는 설정이 불가능하여 배포 서버 에서는 배포 스크립트나 command line에서 gradle 실행 전 직접 환경 변수를 설정해주면 된다.
export BUILD=product //gradle 실행 전 환경 변수 setting
./gradlew clean test
unset BUILD //해당 환경 변수 해제
반면 IDE에서는 Run → Edit Configurations... → Environment variables 부분에 BUILD=product 형태로 설정이 가능하다.
IDE에서 실제 환경 변수 값을 product로 설정하고 전체 테스트를 실행시켜 보면 위 테스트 코드에서 product 메소드만 호출되고 항상 Test Fail이 발생하는 develop 메소드는 호출되지 않아 테스트가 통과된 것을 확인할 수 있다.
처음에는 태그가 활용성이 있나? 싶었는데 빌드 환경 별로 테스트를 분리할 수 있다는 점에서 참으로 유용한 기능인 것 같다. 누군가에게 이 글이 조금이라도 도움이 되길 바라며...
끝.
참고
https://junit.org/junit5/docs/current/user-guide/
'자바 > 테스트' 카테고리의 다른 글
[Mockito] Mocking Framework with JUnit5 (1) | 2023.10.07 |
---|---|
[JUnit] Parameterized Test (0) | 2023.06.24 |
[JUnit] AssertJ Assertions (0) | 2023.06.23 |
[JUnit] JUnit5 Assertions (0) | 2023.06.20 |
[JUnit] 자바 단위 테스트 프레임워크 (0) | 2023.05.31 |
- Total
- Today
- Yesterday
- junit5
- spring aop
- Spring Security
- Front Controller
- Java
- 템플릿 콜백 패턴
- Linux
- spring
- vscode
- github
- QueryDSL
- C++
- ParameterizedTest
- JPA
- 서블릿 컨테이너
- mockito
- 모두의 리눅스
- spring boot
- servlet filter
- 전략 패턴
- 디자인 패턴
- facade 패턴
- SSE
- rest api
- Gitflow
- Transaction
- Assertions
- 단위 테스트
- Git
- FrontController
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 27 | 28 | 29 | 30 |