실습한 코드는 깃허브 링크에서 확인 가능하십니다.
메소드를 개발할 때 다양한 경우를 test 코드를 작성하며 미리 파악할 수 있습니다.
우선, Junit 과 AssertJ의 차이점을 알아보겠습니다.
Junit
자바에 구축된 자동화 테스트 가능한 프레임워크입니다.
다양한 어노테이션(@Test, @Before 등)을 사용하여 테스트 케이스 실행과 테스트 환경을 설정합니다.
AssertJ
자바 테스트 코드의 가독성과 편의성을 높여주는 라이브러리입니다.
메서드 체이닝을 지원하여 더 직관적이고 유연한 테스트 코드 작성을 도와줍니다.
JUnit의 assertEquals만으로 제한될 수 있는 표현력과 달리, assertThat() 메서드와 함께 filteredOn, containsOnly 등 다양한 표현이 가능해 복잡한 조건의 테스트도 쉽게 구현할 수 있습니다.
단위 테스트는 Given/When/Then으로 진행됩니다.
Given(준비): 어떠한 데이터가 준비되었을 때
When(실행): 어떠한 함수를 실행하면
Then(검증): 어떠한 결과가 나와야 한다.
Junit5는 세 개의 서브프로젝트로 이루어져 있습니다.
이 글에서는 가장 최근 버전인 JUnit5
엔진을 사용하겠습니다.
테스트 클래스는 적어도 한 개 이상의 @Test
어노테이션이 달린 메소드를 가져야 합니다.
테스트 클래스는 abstrract이면 안되고, 한 개의 생성자를 가지고 있어야 합니다.
(두 개 이상 작성하면 런타임 시 PreconditionViolationException이 발생합니다.)
public TestAnnotationTests() {}
public TestAnnotationTests(int value) {}
org.junit.platform.commons.PreconditionViolationException: Class [com.unclesubak.section01.test.TestAnnotationTests] must declare a single constructor
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [int arg0] in constructor [public com.unclesubak.section01.test.TestAnnotationTests(int)]
아무런 생성자도 작성하지 않으면 기본 생성자는 컴파일러가 자동으로 추가해줍니다.
@DisplayName
으로 메소드 명이 아닌 설정한 이름으로 표기가 가능합니다.
(미사용 시 메소드명, 한글표기도 가능합니다.)
@Test
@DisplayName("테스트 메소드1")
public void testMethod1() {
}
@DisplayNameGenerator
클래스 레벨에 @DisplayNameGenerator를 붙이게 되면 언더 바를 공백으로 처리하여 테스트 이름을 부여해줍니다. 단, @DisplayName과 중복 작성 시에는 @DisplayName에 부여한 테스트 이름이 우선권을 가집니다.
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class TestAnnotationTests {
@Test
public void 테스트_메소드2() {
}
@Test
@DisplayName("displayName 우선순위 테스트")
public void 테스트_메소드3() {
}
}
각각의 테스트는 실행 순서를 작성 순서로 보장해주지 않습니다.
테스트 메소드 실행 전과 후에 자동 실행되는 설정을 할 수 있습니다.
@BeforeAll
: 테스트가 실행되기 전 딱 한 번만 실행됩니다.@BeforeEach
: 각각의 테스트 메소드가 실행되기 전 실행됩니다.@AfterAll
: 모든 단위 테스트가 완전히 끝난 후 딱 한 번만 실행됩니다.@AfterEach
: 각각의 테스트 메소드가 동작한 직후 동작됩니다.🚨주의해야 할 점
- 모든 테스트 메소드와 라이프사이클 관련 메소드는 abstract이면 안되고, void 형으로 작성해야 합니다.
- 접근제한자는 사용하지 않아도 되지만 (default), private이면 안된다.
@BeforeAll
public static void beforeAll() {
System.out.println("beforeAll");
}
@BeforeEach
public void beforeEach() {
System.out.println("beforeEach");
}
@Test
public void test1() {
System.out.println("test1");
}
@Test
public void test2() {
System.out.println("test2");
}
@AfterEach
public void afterEach() {
System.out.println("afterEach");
}
@AfterAll
public static void afterAll() {
System.out.println("afterAll");
}
부가적인 테스트 조건을 추가하여 테스트를 진행할 수 있습니다.
JUnit에서 기본적으로 제공하는 어노테이션을 종류 별로 정리해보겠습니다.
해당 테스트 메소드를 클래스 레벨에서 제외시킵니다.
JUnit4에선 @Ignore
로 사용됩니다.
@Disabled
@Test
public void testIgnore() {}
@Test
@Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
public void testTimeout() throws InterruptedException {
Thread.sleep(1100);
}
일부 설정으로 특정 Tag들만 테스트할 수 있습니다.
하지만, 규칙이 있습니다.
🚨@Tag를 사용하기 위한 규칙
- 태그는 공백이나 null이 있으면 안됨
- 다음 글자를 포함하면 안됩니다.
,
(
)
&
!
|
@Test
@Tag("development")
public void testTag1() {}
@Test
@Tag("production")
public void testTag2() {}
@Test
@Tags(value = {@Tag("development"), @Tag("performance")})
public void testTag3() {}
우측 상단 실행버튼 옆 드롭다운 박스를 클릭하여 Edit Configurations를 선택합니다.
모듈을 해당 프로젝트의 테스트 경로로 설정합니다. (프로젝트로 설정하면 버퍼 용량 초과 에러 발생합니다.)
Debug/Run Configurations 창 왼쪽 상단에 + 버튼을 눌러서 JUnit을 추가합니다.
필터 이름 설정 후 build and run 부분의 세 번째 드롭다운박스를 선택해서 Tags를 선택하고 필터링할 태그 이름을 입력합니다.
실행하려 하면 설정을 다시 할 것인지 묻는 창이 나오는데 무시하고 그냥 실행하면 해당 태그로 필터링된 테스트만 실행하게 됩니다.
테스트 메소드는 원래는 실행 순서를 보장해주지 않습니다.
하지만 일부 테스트 환경(ex - 통합테스트)에서는 테스트 메소드의 순서가 중요한 경우가 있다고 합니다.
클래스 레벨에 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 어노테이션을 추가합니다.
각 테스트 메소드에 @Order 어노테이션으로 순서를 지정하면 테스트 순서를 설정할 수 있습니다.
(클래스에 작성한 테스트 메소드의 순서는 MethodOrderer에 DisplayName, MethodName, OrderAnnotation, Random 등이 있습니다.)
@Test
@Order(1)
public void testFirst() {}
@Test
@Order(2)
public void testSecond() {}
@Test
@Order(3)
public void testThird() {}
before
after
해당 테스트 메소드를 반복 실행해서 테스트할 수 있습니다.
@RepeatedTest(10)
public void testRepeat() {}
특정 운영체제, JRE, 환경변수에 따라 테스트 수행을 할 수 있습니다.
이 때, 특정 OS에서 무시되는 사유를 disabledReason에 기술하여 테스트코드가 무시되는 사유를 다른 사람도 함께 알아볼 수 있도록 작성해줄 수 있습니다.
🚨설정 가능한 OS들!!
@Test
@EnabledOnOs(value = OS.MAC, disabledReason = "맥 OS 환경에서만 테스트가 가능합니다.")
public void testMAC() {}
@Test
@EnabledOnOs(value = OS.WINDOWS, disabledReason = "Window 환경에서만 테스트가 가능합니다.")
public void testWindow() {}
@Test
@EnabledOnOs(value = {OS.LINUX, OS.WINDOWS}, disabledReason = "Linux 혹은 Window 환경에서만 테스트가 가능합니다.")
public void testLinuxAndWindow() {}
@Test
@DisabledOnOs(value = OS.MAC, disabledReason = "MAC 환경에서는 테스트가 불가능합니다.")
public void testDisabledOnMAC() {}
저는 MAC 환경에서 테스트를 진행하여 다음과 같은 결과가 나왔습니다.
- @EnabledOnJre : 특정 JRE 버전에서만 테스트가 수행됩니다.
- @DisabledOnJre : 특정 JRE을 제외한 버전에서만 테스트가 수행됩니다.
- @EnabledOnForJreRange : min과 max 속성 사이의 버전에서 테스트가 수행됩니다.
- min만 작성 시 min~최신버전까지,
max만 작성 시 이전 버젼부터 max까지만 테스트합니다.- @DisabledOnForJreRange : min과 max 속성 사이를 제외한 버전에서 테스트가 수행됩니다.
@Test
@EnabledOnJre(value = JRE.JAVA_8, disabledReason = "JRE 1.8 환경에서만 테스트가 수행됩니다.")
@Order(5)
public void testJRE8() {}
@Test
@EnabledOnJre(value = {JRE.JAVA_8, JRE.JAVA_11}, disabledReason = "JRE 1.8 과 1.11 환경에서만 테스트가 수행됩니다.")
@Order(6)
public void testJRE8AndJRE11() {}
@Test
@DisabledOnJre(value = JRE.JAVA_11, disabledReason = "JRE 1.11를 제외한 환경에서만 테스트가 수행됩니다.")
@Order(7)
public void testDiabledOnJRE11() {}
@Test
@EnabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_17)
@Order(8)
public void testFromJRE8To17() {}
@Test
@DisabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_17)
public void testDiabledFromJRE8To17() {}
운영체제의 시스템 환경변수에 따라 테스트를 활성화 또는 비활성화 할 수 있습니다.
env
입력하면 조회가 가능합니다. @Test
@EnabledIfEnvironmentVariable(named = "USER", matches = "kimjong-wan")
public void testEnviromentVariable() {}
@Test
@DisabledIfEnvironmentVariable(named = "USER", matches = "kimjong-wan")
public void testDisabledEnviromentVariable() {}
test 어노테이션을 직접 생성해서 사용할 수도 있습니다.
예를 들어, 특정 OS에서 사용할 수 있는 테스트 설정이 복잡하니 간단하게 custum을 해보겠습니다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EnabledOnOs(OS.MAC)
@Test
public @interface MacTest {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EnabledOnOs(OS.WINDOWS)
@Test
public @interface WindowsTest {
}
@WindowsTest
public void testOnWindowsOs() {}
@MacTest
public void testOnMacOS() {}