JUnit5 | 1. Jupiter(JUnit3,4) + Vintage(JUnit5) 2. @Test public 일 필요가 없다(Java Reflection 영향) |
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) | 클래스 에 작성, 언더바를 공백으로/클래스 하위 적용 |
@DisplayName("이름 바꾸기 \uD83D\uDE31") | 특수문자, 한글 가능 |
@DisplayNameGeneration | 메소드, 클래스 레퍼런스 테스트이름표기, DisplayName 보다 우선순위 낮음 |
org.junit.jupiter.api.Assertions.* | AssertEquals.assertEquals(expected, actual, message); assertNotNull(actual) assertTure(boolean) assertThrows(expectedType, executable) assertTimeout(duration, executable) //끝날때까지 실행완료 후 종료 |
람다식 이용 | assertEquals(StudyStatus.DRAFT, study.getStatus(), ()-> "스터디 처음은 상태 "+ StudyStatus.DRAFT); //메시지 부분에 연산이 있을때 람다식으로 만들면 연산횟수가 줄어든다. // 람다를 안쓰면 무조건 연산하고 람다일때는 실패했을때만 연산함. |
assertTimeout vs assertTimeoutPreemptively |
assertTimeoutPreemptively 는 조건시간 되면 종료. 사용주의 assertTimeout(Duration.ofMillis(10), ()->{
assertTimeoutPreemptively(Duration.ofMillis(10), ()->{ //ThreadLocal 사용하는 코드가 있으면 assertTimeoutPreemptively 사용할때 문제 생김. // 다른 쓰레드에 공유가 안되서. 롤백이 안되고 DB반영. |
org.junit.jupiter.api.Assumptions.* |
-assumeTrue(조건) -assumingThat(조건, 테스트) |
@Enabled_와 @Disabled_ |
@Enabled_와 @Disabled_ -OnOS -OnJre -IfSystemProperty -IfEviromentVariable -If |
@Tag |
<plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <groups>fast | slow</groups> </configuration> </plugin |
커스텀 테그 만들기 @interface FastTest |
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Tag("fast") @Test public @interface FastTest{ } |
반복테스트 |
@RepeatedTest |
import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
Assertion 강한 선언, 주장, 권리행사
assert 주장하다.
executables 실현가능한
드레프트 : draft 초안, 복식에서 옷윤곽을 그린 처벌그림, 체육에서 신인선수 선발
백기선님 강의보면서 만든 노트입니다.. 다 직접타이핑이니 오타주의해서보세요~
수업중 사용한 인텔리제이 단축키
Ctrl + Shift + T
테스트 생성
이펜젼시 웹추가
File > Project Structure.
Modules > 프로젝트 > Dependencies > + 클릭 > JARs or directories...
추가할 라이브러리 선택 > OK 클릭
라이브러리 추가 적용 확인 후 OK 클릭
shift + shift 명령어 조회
ctrl + shift +F10 실행
겟터 셋터 단축키 : alt + Insert
임포트 : alt + Enter
1. JUnit
자바개발자가 가장 많이 사용하는 테스팅 프레임워크
- JUnit테스트 작성 자바개발자 93프로 사용. 모키토는 50% 사용
- JUnit5 > 자바 8 이상 >> 2017년 출시
-대체제: TestNG, Spack
JUnit5 모듈
- Platform : 테스트를 실행해주는 런처 제공 TestEngine API 제공
- Jupiter : TestEngine API 구현체로 JUnit5제공
- Vintage: JUnit4와 3을 지원하는 TestEngine구매
https://junit.org/junit5/docs/current/user-guide/
2. JUnit5 시작
스프링 부트 2.2 이상은 기본으로 JUnit5의존성 추가됨.
스프링부트 미사용시
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
기본애노테이션
- @Test
- @BeforeAll / @AfterAll
- @BeforeEach / @AfterEAch
- @Disabled
JUnit5 부터 매서드 Public 일 필요가 없다.
>>Java Reflection >> privte 에도 접근이 가능해 public 일 필요가 없다. 디버거 테스트 위해 일부실행
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3. JUnit 5 : 테스트 이름 표시하기
기본 표기는 이름이 표기 >> 메서드는 카멜보다 언더바이용 추천.
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) >> 언더바를 공백으로/클래스 하위 적용
@DisplayName("이름 바꾸기 \uD83D\uDE31")
@DisplayNameGeneration
- Method와 Class레퍼런스를 사용해서 테스트 이름을 표기하는 방법 설정
- 기본 구현체로 ReplaceUnderscores 제공
@DisplayName
- 어떤 테스트인지 테스트 이름을 보다 쉽게 표현 할 수 있는 방법을 제공하는 애노테이션
@DisplayNameGeneration 보다 우선 순위가 높다.
https://junit.org/junit5/docs/current/user-guide/#writing-tests-display-names
4. JUnit5: Assertion
org.junit.jupiter.api.Assertions.*
실제값이 기대한 값과 같은지 확인 : AssertEquals.assertEquals(expected, actual, message);
값이 null이 아닌지확인 : assertNotNull(actual)
다음 조건이 참(true)인지 확인 : assertTure(boolean)
모든 확인 구문 확인 : assertAll(Executable... executables) >> 여러개 동시 체크
예외 발생 확인 : assertThrows(expectedType, executable)
특정시간 안에 실행이 완료되는지 확인: assertTimeout(duration, executable)
마지막 매개변수로 Supplier<String>타입의 인스턴스를 람다 형태로 제공할 수 있다.
- 복잡한 메시지 생성해야 하는 경우 사용하면 실패한 경우에만 해당 메시지를 만들게 할 수 있다.
AssertJ, Hemcrest, Truth 등의 라이브러리를 사용할수도 있다.
assertEquals(StudyStatus.DRAFT, study.getStatus(), new Supplier<String>() {
@Override
public String get() {
return "스터디 처음은 상태 "+StudyStatus.DRAFT;
}
});
assertEquals(StudyStatus.DRAFT, study.getStatus(), ()-> "스터디 처음은 상태 "+ StudyStatus.DRAFT);
//메시지 부분에 연산이 있을때 람다식으로 만들면 연산횟수가 줄어든다.
// 람다를 안쓰면 무조건 연산하고 람다일때는 실패했을때만 연산함.
assertTimeout(Duration.ofMillis(10), ()->{
new Study(10);
Thread.sleep(1000);
});//끝날때까지 실행완료 후 종료
assertTimeoutPreemptively(Duration.ofMillis(10), ()->{
new Study(10);
Thread.sleep(1000);
});//조건 넘으면 종료
//ThreadLocal 사용하는 코드가 있으면 assertTimeoutPreemptively 사용할때 문제 생김.
// 다른 쓰레드에 공유가 안되서. 롤백이 안되고 DB반영.
cf)
Assertion 강한 선언, 주장, 권리행사
assert 주장하다.
executables 실현가능한
드레프트 : draft 초안, 복식에서 옷윤곽을 그린 처벌그림, 체육에서 신인선수 선발
Supplier, executable
package com.web.javademo1028web;
public class Study {
private StudyStatus status;
private int limit;
public Study(int limit) {
if (limit < 0 ){
throw new IllegalArgumentException("limit 0 초과");
}
this.limit = limit;
this.status = StudyStatus.DRAFT;
}
public StudyStatus getStatus() {
return this.status;
}
public int getLimit() {
return limit;
}
}
package com.web.javademo1028web;
public enum StudyStatus {
DRAFT, STARTED, ENDED
}
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import java.time.Duration;
import java.util.function.Supplier;
import static org.junit.jupiter.api.Assertions.*;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@Test
void create_test_study(){
assertTimeoutPreemptively(Duration.ofMillis(10), ()->{
new Study(10);
Thread.sleep(1000);
});//끝날때까지 실행완료 후 종료
/*IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, ()->new Study(-10));
String msg = exception.getMessage();
System.out.println(msg);
assertEquals("limit 0 초과 아님",exception.getMessage(), "초과문제");
*/
//Study study = new Study(3);
//assertNotNull(study);
//System.out.println("Create");
/*assertEquals(StudyStatus.DRAFT, study.getStatus(), "스터디 처음은 상태 "+ StudyStatus.DRAFT);
assertEquals(StudyStatus.DRAFT, study.getStatus(), new Supplier<String>() {
@Override
public String get() {
return "스터디 처음은 상태 "+StudyStatus.DRAFT;
}
});*/
/*assertEquals(StudyStatus.DRAFT, study.getStatus(), ()-> "스터디 처음은 상태 "+ StudyStatus.DRAFT);
assertTrue(study.getLimit()>0, "스터디 최대 참석가능인원은 0보다 크다");
*/
/*assertAll(
()->assertNotNull(study),
()->assertEquals(StudyStatus.DRAFT, study.getStatus()
,()->"스터디 처음"+StudyStatus.DRAFT),
()->assertTrue(study.getLimit()>0,"스터디 최소 참석 0")
);*/
}
@Test
@DisplayName("이름 바꾸기 \uD83D\uDE31")
void create1(){
//System.out.println("Create1");
}
@Test
@Disabled
void create2(){
//System.out.println("Create2");
}
@BeforeAll//딱한번 호출, static, void
static void beforeAll(){
//System.out.println("before All");
}
@AfterAll
static void afterAll(){
//System.out.println("after all");
}
@BeforeEach
void BeforeEach(){
//System.out.println("BeforeEach");
}
@AfterEach
void AfterEach(){
//System.out.println("AfterEach");
}
}
5. JUnit5: 조건에 따라 테스트 실행하기
특정한 조건(java버전, 환경변수)을 만족하는 경우에 테스트를 실행하는 방법
org.junit.jupiter.api.Assumptions.*
-assumeTrue(조건)
-assumingThat(조건, 테스트)
@Enabled_와 @Disabled_
-OnOS
-OnJre
-IfSystemProperty
-IfEviromentVariable
-If
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.condition.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@Test
@EnabledOnOs({OS.MAC, OS.LINUX})
@DisabledIfEnvironmentVariable(named="TEST_ENV", matches = "DEV")
@EnabledOnJre({JRE.JAVA_8,JRE.JAVA_9})
@EnabledIfEnvironmentVariable(named="TEST_ENV", matches = "LOCAL")
void create_test_study(){
String env = System.getenv("TEST_ENV") ;
System.out.println(env);
assumeTrue("LOCAL".equalsIgnoreCase(env));
//터미넬 export TEST_ENV=LOCAL
assumingThat("local".equalsIgnoreCase(env), ()->{
Study actual = new Study(10);
assertThat(actual.getLimit()).isGreaterThan(0);
});
}
}
6. JUnit5 태깅과 필터링
테스트 그룹을 만들고 원하는 테스트 그룹만 테스트를 실행할수있는기능
@Tag
- 테스트 메소드에 태그를 추가할수 있다.
- 하나의 테스트 메소드에 여러 태그를 사용할수 있다.
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>fast | slow</groups>
</configuration>
</plugin
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>fast</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>ci</id>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>fast | slow</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@Test
@DisplayName("스터디 fast")
@Tag("fast")
void create_test_study(){
Study actual = new Study(10);
assertThat(actual.getLimit()).isGreaterThan(0);
}
@Test
@DisplayName("스터디 slow")
@Tag("slow")
void create_test_study2(){
Study actual = new Study(10);
assertThat(actual.getLimit()).isGreaterThan(0);
}
}
7. JUnit5 커스텀 태그
JUnit5 애노테이션을 조합하여 커스텀 태그를 만들 수 있다.
FastTest.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
@Test
public @interface FastTest{
}
@FestTest
@DisplayName("스터디 만들기 fast")
void create_now_study(){
}
@SlowTest
@DisplayName("스터디 만들기 slow")
void create_new_study_again(){
}
8. JUnit5 테스트 반복하기 1부
@RepeatedTest
-반복횟수와 반복 테스트 이름을 설정할 수 있다.
-{dispplayName}
-{currentRepetition}
-{totalRepetitions}
-RepetitionInfo 타입의 인자를 받을수 있다.
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@DisplayName("스터디만들기")
@RepeatedTest(value =10, name = "{displayName}:{currentRepetition}/{totalRepetitions}")
void create_test_study(RepetitionInfo info){
System.out.println("test"+ info.getCurrentRepetition()+"/"+info.getTotalRepetitions());
}
}
@ParameterizedTest
-테스트에 여러 다른 매개변수를 대입해가며 반복 실행한다.
-{displayName}
-{index}
-{arguments}
-{0}, {1}...
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@DisplayName("JUnit5")
@ParameterizedTest(name="{index} {displayName}_ {0}")
@ValueSource(strings = {"날씨가","많이","더워지고"})
void ParameterizedTest(String msg){
System.out.println("test:"+ msg);
}
}
9. JUnit5 테스트 반복하기 2부
인자 값들의 소스
-@ValueSource
-@NullSource, @EmptySource, @NullAndEmptySource
-@EnumSource
-@MethodSource
-@CvsSource
-@CvsFileSource
-@ArgumentSource
인자 값 타입 변환
- 암묵적 타입변환 : https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests-argument-conversion-implicit
boolean/Boolean |
"true" → true |
byte/Byte |
"15", "0xF", or "017" → (byte) 15 |
char/Character |
"o" → 'o' |
short/Short |
"15", "0xF", or "017" → (short) 15 |
int/Integer |
"15", "0xF", or "017" → 15 |
long/Long |
"15", "0xF", or "017" → 15L |
float/Float |
"1.0" → 1.0f |
double/Double |
"1.0" → 1.0d |
Enum subclass |
"SECONDS" → TimeUnit.SECONDS |
java.io.File |
"/path/to/file" → new File("/path/to/file") |
java.lang.Class |
"java.lang.Integer" → java.lang.Integer.class (use $ for nested classes, e.g. "java.lang.Thread$State") |
java.lang.Class |
"byte" → byte.class (primitive types are supported) |
java.lang.Class |
"char[]" → char[].class (array types are supported) |
java.math.BigDecimal |
"123.456e789" → new BigDecimal("123.456e789") |
java.math.BigInteger |
"1234567890123456789" → new BigInteger("1234567890123456789") |
java.net.URI |
"https://junit.org/" → URI.create("https://junit.org/") |
java.net.URL |
"https://junit.org/" → new URL("https://junit.org/") |
java.nio.charset.Charset |
"UTF-8" → Charset.forName("UTF-8") |
java.nio.file.Path |
"/path/to/file" → Paths.get("/path/to/file") |
java.time.Duration |
"PT3S" → Duration.ofSeconds(3) |
java.time.Instant |
"1970-01-01T00:00:00Z" → Instant.ofEpochMilli(0) |
java.time.LocalDateTime |
"2017-03-14T12:34:56.789" → LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000) |
java.time.LocalDate |
"2017-03-14" → LocalDate.of(2017, 3, 14) |
java.time.LocalTime |
"12:34:56.789" → LocalTime.of(12, 34, 56, 789_000_000) |
java.time.MonthDay |
"--03-14" → MonthDay.of(3, 14) |
java.time.OffsetDateTime |
"2017-03-14T12:34:56.789Z" → OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC) |
java.time.OffsetTime |
"12:34:56.789Z" → OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC) |
java.time.Period |
"P2M6D" → Period.of(0, 2, 6) |
java.time.YearMonth |
"2017-03" → YearMonth.of(2017, 3) |
java.time.Year |
"2017" → Year.of(2017) |
java.time.ZonedDateTime |
"2017-03-14T12:34:56.789Z" → ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC) |
java.time.ZoneId |
"Europe/Berlin" → ZoneId.of("Europe/Berlin") |
java.time.ZoneOffset |
"+02:30" → ZoneOffset.ofHoursMinutes(2, 30) |
java.util.Currency |
"JPY" → Currency.getInstance("JPY") |
java.util.Locale |
"en" → new Locale("en") |
java.util.UUID |
"d043e930-7b3b-48e3-bdbe-5a3ccfb833db" → UUID.fromString("d043e930-7b3b-48e3-bdbe-5a3ccfb833db") |
- 명시적타입변환 :
- SimpleArgumentConverter 상속 은 구현체 제공
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.converter.ArgumentConversionException;
import org.junit.jupiter.params.converter.ConvertWith;
import org.junit.jupiter.params.converter.SimpleArgumentConverter;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.EmptySource;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@DisplayName("JUnit5")
@ParameterizedTest(name="{index} {displayName}_ {0}")
@ValueSource(ints = {10,24,45})
//@EmptySource
//@NullAndEmptySource
//@CsvSource({"java, '자바스터디'","20, '스프링'"})
void ParameterizedTest(@ConvertWith(StudyConverter.class) Study study){
System.out.println("test:"+ study.getLimit());
}
static class StudyConverter extends SimpleArgumentConverter{
@Override
protected Object convert(Object source, Class<?> targetType) throws ArgumentConversionException {
Assertions.assertEquals(Study.class,targetType, "only Study");
return new Study(Integer.parseInt(source.toString()));
}
}
}
- @ConvertWith
인자값 조합
- ArgumentsAccessor
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
import org.junit.jupiter.params.provider.CsvSource;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@DisplayName("JUnit5")
@ParameterizedTest(name = "{index} {displayName}_ {0}")
@CsvSource({"10, '자바스터디'", "20, 스프링"})
void ParameterizedTest(ArgumentsAccessor aa) {
Study study = new Study(aa.getInteger(0), aa.getString(1));
System.out.println(study.toString());
}
}
- 커스텀Accessor
- ArgumentsAggregator 인터페이스 구현
- @AggregateWith
inner static or 퍼블릭이어야
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.aggregator.AggregateWith;
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
import org.junit.jupiter.params.aggregator.ArgumentsAggregationException;
import org.junit.jupiter.params.aggregator.ArgumentsAggregator;
import org.junit.jupiter.params.provider.CsvSource;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@DisplayName("JUnit5")
@ParameterizedTest(name = "{index} {displayName}_ {0}")
@CsvSource({"10, '자바스터디'", "20, 스프링"})
void ParameterizedTest(@AggregateWith(StudyAggregator.class) Study study) {
System.out.println(study.toString());
}
static class StudyAggregator implements ArgumentsAggregator {
@Override
public Object aggregateArguments(ArgumentsAccessor acc, ParameterContext parameterContext) throws ArgumentsAggregationException {
return new Study(acc.getInteger(0), acc.getString(1));
}
}
}
aggregator <여러 회사의 상품이나 서비스에 대한 정보를 모아 하나의 웹사이트에서 제공하는 인터넷 회사·사이트>
10. JUnit5 테스트 인스턴스
JUnit은 테스트 메소드 마다 테스트 인스턴스를 새로 만든다.
- 이것이 기본 전략
- 테스트 메소드를 독립적으로 실행하여 예상치 못한 부작용을 방지하기 위함이다.
- 이 전략을 JUnit 5에서 변경할 수 있다.
@TestInstance(Lifecycle.PER_CLASS)
- 테스트 클래스당 인스턴스를 하나만 만들어 사용한다.
- 경우에 따라 테스트 간에 공유하는 모든 상태를 @BeforeEach 또는@AfterEach에서 초기화할 필요가 있다.
- @BeforeAll 과 @AfterAll을 인스턴스 메소드 또는 인터페이스에 정의한 default메소드로 정의 할 수 도 있다.
11. JUnit5 테스트 순서
실행할 테스트 메소드 특정한 순서에 의해 실행되지만 어떻게 그 순서를 정하는 지 의도적으로 분명히 하지 않는다. (테스트 인스턴스를 테스트마다 새로 만드는 것과 같은이유)
경우에 따라, 특정 순서대로 테스트를 실행하고 싶을 때도 있다. 그 경우에는 테스트 메소드를 원하는 순서에 따라.
실행하도록 @TestInstance(Lifecycle.PER_CLASS)와 함께 @TestMethodOrder를 사용할수 있다.
- MethodOrderer구현체를 설정한다.
- 기본구현체
-Alphanumeric
- OrderAnnotation
- Random
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class StudyTest {
@Order(1)
@Test
void study_test_01() {
System.out.println("study_test_01");
}
@Order(3)
@Test
void study_test_02() {
System.out.println("study_test_02");
}
@Order(2)
@Test
void study_test_03() {
System.out.println("study_test_03");
}
@Order(5)
@Test
void study_test_04() {
System.out.println("study_test_04");
}
}
12. JUnit5 : junit-platform.properties
JUnit설정 파일로 클래스패스 루트 (src/test/resources)에 넣어두면 적용된다.
테스트 인스턴스 라이프사이클설정
junit.jupiter.testinstance.lifecycle.default = per_class
확장팩 자동 감지기능
junit.jupiter.extension.autodetection.enabled=true
@Disabled 무시하고 실행하기
junit.jupiter.conditions.deactivate=org.junit.*DisabledCondition
테스트이름 표기 전략설정
junit.jupiter.displayname.generation.default = \org.junit.jupiter.api.DisplayNameGeneratior$ReplaceUnderscores
13 JUnit5 확장모델
JUnit4의 확장모델 @RunWith(Runner), TestRule, MethodRule
JUnit5의 확장모델 단하나 Extension
확장팩 등록방법
- 선언적인 등록 @ExtenedWith
- 프로그래밍 등록 @RegisterExtension
- 자동 등록 자바 ServiceLoader 이용
확장팩 만드는 방법
- 테스트 실행조건
- 테스트 인스턴스 팩토리
- 테스트 인스턴스 후 처리기
- 테스트 매개변수 리졸버
- 테스트 라이프사이클 콜백
- 예외처리
참고 :
14. JUnit5 JUnit4 마이그레이션
junit-vintage-engne을 의존성으로 추가하면, JUnit5의 junit-platform으로 JUnit 3과 4로 작성된 테스트를 실행할수 있다.
- @Rule은 기본적으로 지원하지 않지만, junit-jupiter-migrationsupport 모듈이 제공하는 @EnableRuleMigrationSupport를 사용하면 다음타입의 룰을 지원한다.
- ExternalResource
- Verifier
- ExpectedException
JUnit4 | JUnit5 |
@Category(Class) | @Tag(String) |
@RunWith, @Rule, @ClassRule | @ExtendWith , @RegisterExtension |
@Ignore | @Disabled |
@Before, @After, @BeforeClass, @AfterClass | @BeforeEach, @AfterEach, @BefreAll, @AfterAll |
15. JUnit5 연습문제
1. 테스트 이름을 표기하는 방법으로 공백, 특수 문자 등을 자유롭게 쓸 수 있는 애노테이션은?
답: @DisplayName
2. JUnit 5, jupiter는 크게 세가지 모듈로 나눌 수 있습니다. 다음 중에서 테스트를 실행하는 런처와 테스트 엔진의 API를 제공하는 모듈은 무엇일까요?
① junit jupiter ② junit vintage ③ junit platform
답:①
3. JUnit 5에서 테스트 그룹을 만들고 필터링 하여 실행하는데 사용하는 애노테이션은?
답: @Tag
4. 다음 코드는 여러 Assertion을 모두 실행하려는 테스트 코드입니다. 빈칸에 적절한 코드는 무엇인가요?
@Test @DisplayName("스터디 만들기") void create_new_study() { Study actual = new Study(1, "테스트 스터디"); ________( () -> assertEquals(1, actual.getLimit()), () -> assertEquals("테스트 스터디", actual.getName()), () -> assertEquals(StudyStatus.DRAFT, actual.getStatus()) ); } |
답: assertAll
5. 다음은 JUnit 5가 제공하는 애노테이션으로 컴포짓 애노테이션을 만드는 코드입니다. 이 애노테이션에 적절한 Rention 전략은 무엇인가요?
@Target(ElementType.METHOD) @Retention(______________) @Test @Tag("fast") public @interface FastTest { } |
답: Retention : 메모리 언제까지 유지? RetentionPolicy.RUNTIME : 런타임시까지 유지
6. 다음 중 JUnit 5가 제공하는 확장팩 등록 방법이 아닌것은?
① @ExtendWith
② @Rule
③ @RegisterExtension
④ ServiceLoader
답 :
- 선언적인 등록 @ExtenedWith
- 프로그래밍 등록 @RegisterExtension
- 자동 등록 자바 ServiceLoader 이용
7. 다음 코드는 유즈케이스 테스트를 작성한 것입니다. 다음 빈 칸에 적절한 코드는?
@TestInstance(TestInstance.Lifecycle.________) @TestMethodOrder(MethodOrderer.___________.class) public class StudyCreateUsecaseTest { private Study study; @Order(1) @Test @DisplayName("스터디 만들기") public void create_study() { study = new Study(10, "자바"); assertEquals(StudyStatus.DRAFT, study.getStatus()); } @Order(2) @Test @DisplayName("스터디 공개") public void publish_study() { study.publish(); assertEquals(StudyStatus.OPENED, study.getStatus()); assertNotNull(study.getOpenedDateTime()); } } |
답: @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
8. 다음은 여러 매개변수를 바꿔가며 동일한 테스트를 실행하는 코드입니다. 빈칸에 적잘한 코드는?
@Order(4) @DisplayName("스터디 만들기") @________________(name = "{index} {displayName} message={0}") @CsvSource({"10, '자바 스터디'", "20, 스프링"}) void parameterizedTest(@___________(StudyAggregator.class) Study study) { System.out.println(study); } static class StudyAggregator implements ArgumentsAggregator { @Override public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) throws ArgumentsAggregationException { return new Study(accessor.getInteger(0), accessor.getString(1)); } } |
답 : @ParameterizedTest(name="{index} {displayName}_ {0}")
@AggregateWith
package com.web.javademo1028web;
import org.junit.jupiter.api.*;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@Test
@DisplayName("스터디 fast")
@Tag("fast")
void create_test_study(){
Study actual = new Study(10);
assertThat(actual.getLimit()).isGreaterThan(0);
}
@Test
@DisplayName("스터디 slow")
@Tag("slow")
void create_test_study2(){
Study actual = new Study(10);
assertThat(actual.getLimit()).isGreaterThan(0);
}
}
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>fast</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>ci</id>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>fast | slow</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>