Java의 단위 테스트(Unit Test)
테스트 코드를 작성하는 이유
개발 단계에서 테스트 코드를 작성하는 이유는 정말 다양하다.
- 개발 과정에서 미리 문제를 발견할 수 있다.
- 코드에 잠재되어있떤 문제를 발견하는데 큰 도움이 된다.
- 리팩토링의 리스크가 줄어든다.
- 서비스 업데이트를 하거나 요구사항이 변경되면 코드가 추거되거나 수정되는데, 그 코드에 연관된 다른 코드에 까지 영향을 준다.
- 코드를 수정하는 과정에서의 버그들이나 잘못된 점을 빠르게 잡을 수 있다.
- 애플리케이션을 가동해서 직접 테스트하는 것보다 테스트를 빠르게 진행할 수 있다.
- 테스트 코드를 공유함으로써 이 테스트와 이 기능이 무엇을 하는지 알 수 있다.
- 하나의 명세 문서로서의 기능을 수행한다
- 몇 가지 프레임워크에 맞춰 테스트 코드를 작성하면 좋은 코드를 생산할 수 있다.
- 코드가 작성된 목적을 명확하게 표현할 수 있으며, 불필요한 내용이 추가되는 것을 방지한다.
Java의 단위테스트의 패턴
단위 테스트의 추세는 주로 한개의 단위 테스트를 3가지로 나눠서 처리하는,
given - when - then
패턴을 사용한다고 한다.
- given(준비) : 어떤 데이터를 준비한다.
- when(실행) : 어떤 기능 혹은 메서드를 실행한다 ( 조건을 지정하여 실행한다.)
- then(검증) : 어떠한 결과가 나오면 그 결과를 검증한다
- verify : 메서드가 호출된 횟수, 타임아웃 시간 등을 검사 할떄 사용한다
@Test
void test() {
//given
...
//when
....
//then
.....
}
JUnit5
자바언어에서 사용되는 대표적인 테스트 프레임워크로서 단위 테스트를 위한 도구를 제공한다.
단위 테스트 뿐만이 아닌 통합 테스트를 기능도 제공한다.
- JUnit5의 구성
JUnit5는 크게 3가지로 구성되어 있다.
Junit Platform
JVM에서 테스트를 시작하기 위한 뼈대 역할.
테스트를 실행해주는 런쳐를 제공한다.
테스트 엔진 API를 제공한다 - 인터페이스 형태로
- Test Engine : 테스트를 발견하고 테스트 계획을 생성한다
- 테스트를 발견하고 테스트를 수행하며 그결과를 보고한다
- 각종 IDE와의 연동을 보조하는 역할 수행(IDE 콘솔 출력 등)
- PlatForm에는 TestEngine API, Console Launcher, JUnit 4 Based Runner등이 포함되어 있다.
JUnit Jupiter
Junit Platform에서 제공하는 Test Engine API 인터페이스를 구현한 구현체를 포함하고 있다.
테스트의 실제 구현체는 별도 모듈의 역할을 수행하는데 그중 하나가 Jupiter Engine이다
Jupter Engine은 Juptier API를 활용해서 작성한 테스트 코드를 발견하고 실행하는 역할을 수행한다.
JUnit Vintage
Junit 3, 4 에 대한 테스트 엔진 API를 구현하며, 3, 4 를 지원한다.
기존에 작성된 3, 4 버전에 테스트 코드를 실행할 때 사용된다.
JUnit 5 라이브러리 의존성 추가
- Build.gradle에 다음과 같이 추가
Spring Boot 2.2 버전 이상의 경우
testImplementation("org.springframework.boot:spring-boot-starter-test")
test {
useJUnitPlatform()
}
Spring Boot 2.1 버전 이하의 경우
// junit5
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude module : 'junit'
}
testImplementation("org.junit.jupiter:junit-jupiter-api")
testCompile("org.junit.jupiter:junit-jupiter-params")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
test {
useJUnitPlatform()
}
org.springframework.boot:spring-boot-starter-test 의존성 추가시
SpringBoot 2.1 이하의 버전은 Junit4를 기본으로 사용
SpringBoot 2.2 이상의 버전은 Junit5를 기본으로 사용
- 2.2 버전 이상의 스프링 부트 프로젝트를 만든다면 기본으로 JUni5 의존성이 추가된다.
spring-boot-starter-test 라이브러리
JUnit, Mockito, assertJ 등의 다양한 테스트 도구를 제공한다.
대표적인 라이브러리는 다음과 같다
- JUnit 5 : 자바 애플리케이션의 단위 테스트 지원
- Spring Test & Spring Boot Test : 스프링 부트 애플리케이션에 대한 유틸리와 통합 테스트를 지원한다
- AssertJ : 다양한 단정문(assert)을 지원하는 라이브러리
- Hamcrest: Matcher를 지원하는 라이브러리
- Mockto : 자바 Mock 객체를 지원하는 프레임워크
- JSONassert: JSON용 단정문 라이브러리
- JsonPath : JSON용 XPath를 지원한다.
JUnit의 기본 어노테이션
기본적으로 테스트는 테스트 메서드에 @Test
어노테이션을 붙이면 실행 가능한 상태가 되어
독립적으로 실행한다.
JUnit 5부터는 테스트 메서드에 public 키워드를 붙이지 않고 실행할 수 있다.
기본 어노테이션
- @Test : 테스트 코드를 포함한 테스트 메서드를 정의한다.
- @BeforeAll : 테스트를 시작하기 전에 호출되는 메서드를 정의한다.
- 테스트 실행전 딱 한번 실행, 반드시 static void를 사용하여 정의한다
- @BeforeEach : 각 테스트 메서드가 실행되기 전에 동작하는 메서드를 정의한다
- static일 필요는 없다
- @AfterAll : 각 테스트가 실행된 이후에 호출되는 메서드
- 반드시 static void를 사용하여 정의한다
- @AfterEach : 각 테스트 메서드가 종료되면서 호출되는 메서드를 정의한다
- @Disabled : 특정 테스트를 무시한다. 실행하고 싶지 않은 메서드에 사용한다
- @Ignore 어노테이션이랑 비슷한 동작을 한다.
테스트 이름 표시하기
- @DisplayNameGeneration
- Method와 Class 레퍼런스를 사용해서 테스트 이름을 표기하는 방법을 설정한다
Test Results
의 클래스 밑에 메서드 이름들이 옵션에 따라 바뀐다.- 클래스를 레퍼런스 하면 클래스 내 모든 테스트 메서드에 적용된다
- 기본 제공체로 ReplaceUnderscores를 제공하며, 이 제공체는 언더스코어를 다 스페이스바(빈공백)으로 바꾸어 준다
- @DisplayName
- 어떤 테스트 인지 테스트 이름을 보다 쉽게 표현해줘서 Test Reulsts 에서 더 쉽게 확인이 가능하다.
- @DisplayNameGeneration 보다 우선순위가 높다
JUnit 5 Assertion
실제 테스트에서 검증하고자 할떄 사용하는 기능
- 패키지 org.junit.jupiter.api.Assertions
메서드 명 | 기능 |
---|---|
assertEqulas(expected, actual) | 실제 값이 기대한 값과 같은지 확인 |
assertNotNull(actual) | 값이 null이 아닌지 확인 |
assertTrue(boolean) | 다음 조건이 참(true)인지 확인 |
assertAll(executables...) | 모든 확인 구문 확인 |
assertThrows(expectedType, executable) | 예외 발생 확인. 어떤 예외가 발생하는지 비교, 파라미터를 리턴 받아서 어떤 예외인지 알 수 있다. |
assertTimeout(duration, executable) | 특정 시간 안에 실행이 완료되는지 확인 |
- 메서드 마지막 매개변수로 메시지를 줄 수 있는데,
- Supplier < String > 타입의 인스턴스를 람다 형태로 제공할 수 있다
- 첫번째 assert 깨지면 다음 assert는 실행 되지 않는다.
- assertAll을 사용하면 모든 테스트가 실행되게 할 수 있다.
- assertTimeOut
- AssertJ, Hemcrest, Truth 등의 라이브러리를 사용할 수도 있다
JUnit 5 조건에 따라 테스트 실행하기
특정한 조건을 만족하는 경우에 테스트를 실행하는 방법.
org.junit.jupiter.api.Assumptions.*
assumeTrue(조건)
assumingThat(조건, 테스트)
특정한 조건을 만족하는 경우에만 테스트를 실행한다
class TestClass {
@Test
@EnabledOnOs({OS.MAC, OS.WINDOWS})
void EnabledOnOs() {
System.out.println("운영체제에 따라서 실행...");
}
@Test
@EnabledOnJre({JRE.JAVA_10, JRE.JAVA_11})
void EnabledOnJre() {
System.out.println("JRE 버전에 따라서 실행...");
}
@Test
@EnabledIfEnvironmentVariable(named = "local", matches = "local")
void EnabledOnIf() {
System.out.println("Env 에따라서 실행...");
}
- @Enabled___ 와 @Disabled___
- OnOS
- OnJre
- IfSystemProperty
- IfEnvironmentVariable
- If
JUnit 5 태깅과 필터링
테스트 태깅 : 여러 테스트들을 테스트 그룹을 만들고 원하는 테스트 그룹만 테스트를 실행할 수 있는 기능.
- @Tag
- 테스트 메소드에 태그를 추가할 수 있다.
- 하나의 테스트 메소드에 여러 태그를 사용할 수 있다
인텔리J에서 특정 태그로 테스트 필터링 하는 방법 특정 테스트 Edit Configuration -> Test kind 를 Tags로 수정 -> Tag expression 에 태그명 입력
메이븐에서 테스트 필터링 하는 방법
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>fast | slow</groups>
</configuration>
</plugin>
-> fast나 slow 태그가 붙은 테스트만 실행
JUnit 5 커스텀 태그
JUnit 5 애노테이션을 조합하여 커스텀 태그를 만들 수 있다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Test
@Tag("fast")
public @interface FastTest {...}// 우리가 새로 만든 어노테이션
@Test
@DisplayName("fast 그룹 테스트 1")
@Tag("fast")
void test_fast1() {...}
위를 다음과 같이 변경이 가능하다.
@FastTest
@DisplayName("fast 그룹 테스트 1")
void custom_test_fast1() {...}
참고
- https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests
- 인프런 백기선님 강의 더 자바, 애플리케이션을 테스트하는 다양한 방법
'테스트 > JUnit' 카테고리의 다른 글
Spring Boot JPA Test @DataJpaTest 기본 설정 (3) | 2022.08.27 |
---|---|
JUnit5 - Java에서의 단위테스트5 Mock, Mockito, Stup (0) | 2022.08.21 |
JUnit5 - Java에서의 단위테스트4 Mock, Mockito (0) | 2022.08.21 |
JUnit5 - Java에서의 단위테스트3 - 테스트 인스턴스들 (0) | 2022.08.21 |
JUnit5 - Java에서의 단위테스트2 - 테스트 반복 (0) | 2022.08.21 |