: 테스트 명시
: 모든 테스트 전 or 후 한 번만 실행한다.
: 접근 제한자는 private 불가능, default 가능이다. 또한 return은 없어야 한다. 즉 static void 로 선언하면 된다고 보면 된다.
: 각 테스트 후 실행한다.
: 이 테스트는 실행하지 않는다.(좋은 방법은 아니다)
: 어떻게 DisplayName을 생성할 것인지 전략을 표현할 때 사용한다. 이 애노테이션은 클래스, 메소드에 사용할 수 있다.
: 기본 구현체로 ReplaceUnderscores 제공한다.
: 어떤 테스트인지 테스트 이름을 쉽게 표현할 수 있는 방법을 제공한다. 위의 @DisplayNameGeneration보다 우선 순위가 높다.
아래와 같이 expected, actual, 에러 message 순으로 작성할 수 있다.
assertEquals(StudyStatus.DRAFT, study.getStatus(), () -> "스터디를 처음 만들면 상태 값이 DRAFT여야 한다.");
message에서 문자열만 넘기는 경우에는 테스트가 성공 여부에 관계없이 항상 문자열 연산(+연산)을 실행하지만 람다식은 실패할 시에만 실행한다.
즉, 람다식을 사용하는게 성능상으로 유리할 수 있다.
값이 null이 아닌지 확인한다.
다음 조건이 참인지 확인한다.
모든 확인 구문 확인한다.
assertAll(
() -> assertNotNull(study),
() -> assertEquals(StudyStatus.DRAFT, study.getStatus(), ()-> "스터디를 처음 만들면 상태 값이 DRAFT여야 한다."),
() -> assertTrue(study.getLimit() > 0, "스터디 최대 참석 인원은 0보다 커야 한다.")
);
한 테스트 내에서 하나의 테스트가 실패하게 된다면, 다음 테스트는 무시가 되는데 assertAll을 사용할 경우 모든 테스트의 결과를 반영해 준다.
예외 발생을 확인한다.
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> new Study(-10));
assertEquals("limit는 0보다 커야 한다.", ex.getMessage());
결과 값을 리턴받아 에러 메시지와 비교할 수도 있다.
특정 시간 안에 실행이 완료되는지 확인한다.
assertTimeout(Duration.ofMillis(100), () -> {
new Study(10);
Thread.sleep(300);
});
위의 예제대로 실행을 한다면 100ms가 넘더라도 실제 코드가 종료되어야 테스트가 종료된다. 따라서 특정 시간이 지나면 테스트가 즉시 종료될 수 있는 assertTimeoutPreemptively
를 사용할 수 있다.
하지만 assertTimeoutPreemptively
을 사용할 때 코드 블럭을 사용할 경우, 코드 블럭은 별도의 스레드에서 실행하기에 예상치 못한 결과가 발생할 수 있다. 즉 Thread와 관련없는 경우에만 사용해야 한다.
org.junit.jupiter.api.Assumptions.*
assumeTrue(조건)
//조건
Assumptions.assumeTrue("LOCAL".equalsIgnoreCase(test_env));
//만족하면 테스트
Study actual = new Study(10);
org.assertj.core.api.Assertions.assertThat(actual.getLimit()).isGreaterThan(0);
assumingThat(조건, 테스트)
//조건, 테스트
Assumptions.assumingThat("LOCAL".equalsIgnoreCase(test_env), () -> {
Study actual2 = new Study(10);
org.assertj.core.api.Assertions.assertThat(actual2.getLimit()).isGreaterThan(0);
});
애노테이션 사용
//실행할 운영체제
@EnabledOnOs({OS.MAC, OS.LINUX})
//실행할 java 버전
@EnabledOnJre({JRE.JAVA_8})
//무시할 운영체제 선택
@DisabledOnOs(OS.MAC)
@DisplayName("스터디 만들기 테스트")
@RepeatedTest(value = 5, name = "{displayName}, {currentRepetition}/{totalRepetitions}") // (1)
// (2)
void repeatTeat(RepetitionInfo repetitionInfo){
System.out.println("test" + repetitionInfo.getCurrentRepetition() + "/" + repetitionInfo.getTotalRepetitions());
}
@ParameterizedTest(name = "{index} {displayName} {0}") // (1)
@ValueSource(strings = {"날씨가", "많이", "더워지고", "있당"}) // (2)
void parameterizedTest(String message){
System.out.println(message);
}
@DisplayName("하이용")
@ParameterizedTest(name = "{displayName}")
@ValueSource(ints = {10, 40, 30})
void parameterizedTest(@ConvertWith(StudyConverter.class) String message){
System.out.println(message);
}
//형변환
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()));
}
}
SimpleArgumnetConverter
를 구현해서 형 변환을 한다. @DisplayName("csv 테스트")
@ParameterizedTest(name = "{displayName} {index}")
@CsvSource({"10, '자바 스터디'", "20, 스프링"})
void parameterizedTestUsingCsv(Integer limit, String name){
System.out.println(new Study(limit, name));
}
//Study{status=null, limit=20, name='스프링'} 출력
: 인자 값을 조합하는 방식
@DisplayName("argumentAccessor 테스트")
@ParameterizedTest(name = "{displayName} {index}")
@CsvSource({"10, '자바 스터디'", "20, 스프링"})
void parameterizedTest_argumentsAccessor(ArgumentsAccessor argumentsAccessor){
Study study = new Study(argumentsAccessor.getInteger(0), argumentsAccessor.getString(1));
System.out.println(study);
}
//Study{status=null, limit=10, name='자바 스터디'}
또한 위의 코드를 아래와 같이 구현을 통해 줄일 수 있다.
@DisplayName("argumentAccessor 테스트")
@ParameterizedTest(name = "{displayName} {index}")
@CsvSource({"10, '자바 스터디'", "20, 스프링"})
void parameterizedTest_argumentsAccessor(@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)
을 통해 클래스 당 인스턴스 하나를 만들어서 변경할 수 있다.
JUnit은 서로 간의 의존성이 없도록 하기 위해 테스트 순서는 언제든 변경이 될 수 있다. 하지만 원하는 순서에 따라 테스트를 진행하는 방식을 제공한다.
테스트 클래스에 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
을 붙이고 각 테스트에 @Order(1)
을 붙이면 된다.
src/test/resources/
하단에 junit-platform.properties
추가
junit.jupiter.testinstance.lifecycle.default = per_class
junit.jupiter.extensions.autodetection.enabled = true
junit.jupiter.conditions.deactivate = org.junit.*DisabledCondition
@disabled
무시Display
전략
junit.jupiter.displayname.generator.default = org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscore
_
을 공백으로 변경한다.