자바 개발자가 가장 많이 사용하는 테스팅 프레임워크
JUnit5의 세부 모듈
스프링 부트 프로젝트를 사용하지 않는다면?
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
기본 어노테이션
@Test
void create(){
Study study=new Study();
assertNotNull(study);
System.out.println("create");
}
@Test
@Disabled //테스트가 실행되지 않음
void create1(){
System.out.println("create1");
}
//모든 테스트 수행전 딱 한번. 반드시 static 메서드를 사용해야함.(default는 되고, return은 없어야됨)
@BeforeAll
static void beforeAll(){
System.out.println("before all");
}
//모든 테스트 실행후 딱 한번
@AfterAll
static void afterAll(){
System.out.println("after all");
}
//각각의 테스트 수행전 딱 한번
@BeforeEach
void beforeEach(){
System.out.println("Before each");
}
//각각의 테스트 수행후 딱 한번
@AfterEach
void afterEach(){
System.out.println("after each");
}
@DisplayNameGeneration
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@Test
void create_new_study(){
Study study=new Study();
assertNotNull(study);
System.out.println("create");
}
@Test
void create_new_study_again(){
System.out.println("create1");
}
}
@DisplayName
@Test
@DisplayName("스터디 만들기 \uD83D\uDE31")
void create_new_study(){
Study study=new Study();
assertNotNull(study);
System.out.println("create");
}
org.junit.jupiter.api.Assertions.*
실제 값이 기대한 값과 같은지 확인 | assertEquals(expected, actual) |
---|---|
값이 null이 아닌지 확인 | asserNotNull(actual) |
다음 조건이 참인지 확인 | assertTrue(boolean) |
모든 확인 구문 확인 | assertAll(executables...) |
예외 발생 확인 | assertThrows(expectedType, executable) |
특정 시간 안에 실행이 완료되는지 확인 | assertTimeout(duration, executable) |
void create_new_study(){
Study study=new Study(-10);
assertAll(
()->assertNotNull(study),
//첫번째 파라미터: 기대값, 두번째 파라미터: 실제값, 세번쨰 파라미터: 오류 발생시 메시지(람다식으로 만들면 실패 했을때만 문자열 연산 수행하고, 람다식으로 만들지 않을시 무조건 문자열 연산을 수행한다)
()->assertEquals(StudyStatus.ENDED, study.getStatus(), () -> "스터디를 처음 만들면 상태값이 DRAFT이어야 한다."),
()->assertTrue(study.getLimit()>0,"스터디 최대 참석 가능 인원은 0보다 커야 한다.")
);
assertThrows(IllegalArgumentException.class,()->new Study(-10));
assertTimeout(Duration.ofMillis(100),()->{
new Study(10);
Thread.sleep(300);
});
System.out.println("create");
}
마지막 매개변수로 Supplier 타입의 인스턴스를 람다 형태로 제공할 수 있음
AssertJ, Hemcrest, Truth 등의 라이브러리도 사용 가능
특정한 조건을 만족하는 경우 테스트를 실행하는 방법
org.junit.jupiter.api.Assumptions.*
assumeTrue(조건)
assumingThat(조건, 테스트)
void create_new_study() throws InterruptedException {
String test_env=System.getenv("TEST_ENV");
System.out.println(test_env);
assumeTrue("LOCAL".equalsIgnoreCase(test_env));
assumingThat("LOCAL".equalsIgnoreCase(test_env),()->{
System.out.println("local");
Study actual=new Study(100);
});
assumingThat("always".equalsIgnoreCase(test_env),()->{
System.out.println("always");
Study actual=new Study(10);
});
}
@Enabled와 @Disabled
@EnabledIfEnvironmentVariable(named="TEST_ENV",matches="local")
테스트 그룹을 만들고 원하는 테스트 그룹만 테스트를 실행할 수 있는 기능
@Tag
인텔리제이에서 특정 태그로 테스트 필터링
클릭후 "Edit Configuration" 클릭!
메이븐에서 테스트 필터링
<profiles>
<profile>
<id>default</id>
<activation>
<activativeByDefault>true</activativeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>fast | slow</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
JUnit5 어노테이션을 조합해 커스텀 태그를 만들 수 있음
SlowTest.java
@Target(ElementType.METHOD) //해당 어노테이션은 메소드에 쓸 수 있음
@Retention(RetentionPolicy.RUNTIME) //해당 어노테이션의 정보를 런타임까지 기억
@Test
@Tag("slow")
public @interface SlowTest {
}
@SlowTest
@DisplayName("스터디 만들기 slow")
void create_new_study_again(){
System.out.println("create1");
}
이렇게 하면 태그를 붙이지 않고, 해당 어노테이션을 가져다 쓰기만 하면 됨
@RepeatedTest
반복 횟수와 반복 테스트 이름을 설정할 수 있음
RepetitionInfo 타입의 인자를 받을 수 있음
@DisplayName("스터디 만들기")
@RepeatedTest(value=10,name="{displayName},{currentRepetition}/{totalRepetitions}")
void repeatTest(RepetitionInfo repetitionInfo){
System.out.println("test "+repetitionInfo.getCurrentRepetition()+"/"+repetitionInfo.getTotalRepetitions());
}
@ParameterizedTest
테스트에 여러 다른 매개변수를 대입하며 반복 실행
@DisplayName("스터디 만들기")
@ParameterizedTest(name="{index} {displayName} msg={0}")
@ValueSource(strings={"날씨가","많이","추워지고","있네요"})
void parameterizedTest(String msg){
System.out.println(msg);
}
@ParameterizedTest의 인자 값들의 소스
@ParameterizedTest의 인자 값 타입 변환
암묵적인 타입 변환
명시적인 타입 변환
SimpleArgumentConverter 상속 받은 구현체 제공
@ConvertWith
@DisplayName("스터디 만들기")
@ParameterizedTest(name="{index} {displayName} msg={0}")
@ValueSource(ints={10,20,40})
void parameterizedTest(@ConvertWith(StudyConverter.class)Study study){
System.out.println(study.getLimit());
}
static class StudyConverter extends SimpleArgumentConverter{
@Override
protected Object convert(Object source, Class<?> targetType) throws ArgumentConversionException {
assertEquals(Study.class,targetType,"Can only convert to Study");
return new Study(Integer.parseInt(source.toString()));
}
}
@ParameterizedTest의 인자 값 조합
ArgumentsAccessor
@DisplayName("스터디 만들기")
@ParameterizedTest(name="{index} {displayName} msg={0}")
@CsvSource({"10 '자바 스터디'","20,스프링"})
void parameterizedTest(ArgumentsAccessor argumentsAccessor){
Study study=new Study(argumentsAccessor.getInteger(0),argumentsAccessor.getString(1));
System.out.println(study);
}
커스텀 Accessor
@DisplayName("스터디 만들기")
@ParameterizedTest(name="{index} {displayName} msg={0}")
@CsvSource({"10 '자바 스터디'","20,스프링"})
void parameterizedTest(@AggregateWith(StudyAggregator.class)Study study){
System.out.println(study);
}
static class StudyAggregator implements ArgumentsAggregator{
@Override
public Object aggregateArguments(ArgumentsAccessor argumentsAccessor, ParameterContext parameterContext) throws ArgumentsAggregationException {
return new Study(argumentsAccessor.getInteger(0),argumentsAccessor.getString(1));
}
}
JUnit은 테스트 메소드마다 테스트 인스턴스를 새로 만듦
@TestInstance(Lifecycle.PER_CLASS)
실행할 테스트 메소드는 특정한 순서에 의해 실행되지만, 경우에 따라 테스트 메소드를 원하는 순서에 따라 실행할 수 있다
@TestMethodOrder
테스트 클래스당 하나의 인스턴스를 만들고
MethodOrderer 구현체 설정
기본 구현체
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class StudyTest {
@Order(1)
@EnabledOnOs(OS.WINDOWS)
@Test
@Tag("fast")
void create_new_study() throws InterruptedException {
String test_env=System.getenv("TEST_ENV");
System.out.println(test_env);
}
JUnit 설정 파일로 classpath root(src/test/resources/)에 넣어두면 적용됨
전체 테스트에 일괄 설정됨
Project Structure에 가서 해당 파일을 Test Resources 파일로 등록해야함
테스트 인스턴스 라이프사이클 설정
junit.jupiter.testinstance.lifecycle.default=per_class
확장팩 자동 감지 기능
junut.jupiter.extensions.autodetection.enabled=true
@Disabled 무시하고 실행하기
junit.jupiter.conditions.deactivate=org.junit.*DisabledCondition
테스트 이름 표기 전략 설정
junit.jupiter.displayname.generator.default=\org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores
JUnit4의 확장 모델은 @RunWith(Runner), TestRule, MethodRule
JUnit5의 확장 모델은 Extension
확장팩 등록 방법
선언적인 등록 @ExtendWith(파라미터 전달 불가)
프로그래밍 등록 @RegisterExtension(파라미터 전달 가능)
등록하고자 하는 클래스 필드에 작성
@RegisterExtension
static FindSlowTestExtension findSlowTestExtension= new FindSlowTestExtension(1000L);
자동 등록 자바 ServiceLoader 이용
확장팩 만드는 법
JUnit4 | JUnit5 |
---|---|
@Category(Class) | @Tag(String) |
@RunWith, @Rule, @ClassRule | @ExtendWith, @RegisterExtension |
@Ignore | @Disabled |
@Before, @After, @BeforeClass, @AfterClass | @BeforeEach, @AfterEach, @BeforeAll, @AfterAll |