-
1. JUnit5Test/더 자바, 테스트하는 다양한 방법 2022. 10. 27. 17:28
JUnit5 기본 애노테이션
@Test - test 메서드에 사용 @BeforeAll - 모든 테스트가 실행하기 전에 딱 한번만 호출
- static void로 작성@AfterAll - 모든 테스트가 실행된 후 딱 한번만 호출
- static void로 작성@BeforeEach - 각각의 테스트 실행 전 실행 @AfterEach - 각각의 테스트 실행 후 실행 @Disabled - 테스트를 실행하고 싶지 않을 때 사용 JUnit5 테스트 이름 표기
@DisplaynameGeneration - Method와 Class 레퍼런스를 사용해서 테스트 이름을 표기하는 방법 설정
- 기본 구현체로 ReplaceUnderscores 제공@DisplayName - 어떤 테스트인지 테스트 이름을 보다 쉽게 표현할 수 있는 방법을 제공하는 애노테이션
- @DisplayNameGeneration 보다 우선순위가 높다.JUnit5 Assertion
- org.junit.jupiter.api.Assertions.*
assertEquals( expected, actual ) 실제 값이 기대한 값과 같은지 확인 assertNotNull( actual ) 값이 null이 아닌지 확인 assertTrue( boolean ) 다음 조건이 참(true)인지 확인 assertAll( executables... ) 모든 확인 구문 확인 assertThrows( expectedType, executable ) 예외 발생 확인 assertTimeout( duration, executable ) 특정 시간 안에 실행이 완료되는지 확인 - 마지막 매개변수(메시지)로 Supplier<String> 타입의 인스턴스를 람다 형태로 제공할 수 있다.
ㆍ복잡한 메시지 생성해야하는 경우 사용하면 실패한 경우에만 해당 메시지를 만들 수 있다.
JUnit5 조건에 따라 테스트 실행하기
- org.junit.jupiter.api.Assumptions.*
ㆍassumeTrue(조건)
@Test @DisplayName("스터디 만들기") void create_new_study() { String test_env = System.getenv("TEST_ENV"); System.out.println(test_env); assumeTrue("LOCAL".equalsIgnoreCase(test_env)); // 테스트 환경이 로컬일때만 다음 코드를 실행한다. Study study = new Study(-10); assumingThat("LOCAL".equalsIgnoreCase(test_env), () -> { }); // 조건에 따른 로직 수행 }
ㆍassumingThat(조건, 테스트)
- @Enableed___ 와 @Disabled__
ㆍOnOS
ㆍOnJre
ㆍIfSystemProperty
ㆍIfEnvironmentVariable
ㆍIf
@EnabledOnOs({OS.MAC,OS.LINUX}) @DisabledOnOs(OS.MAC) @EnabledOnJre({JRE.JAVA_8,JRE.JAVA_11}) @EnabledIfEnvironmentVariable(named = "TEST_ENV", matches = "LOCAL")
JUnit5 태깅과 필터링
- 테스트 그룹을 만들고 원하는 테스트 그룹만 테스트를 실행할 수 있는 기능
- @Tag
ㆍ테스트 메소드에 태그를 추가할 수 있다
ㆍ하나의 테스트 메소드에 여러 태그를 사용할 수 있다.
ㆍtest kind - Tags 설정 / Tag expression - 태그 이름 설정
<profiles> <profile> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <artifactId>maven-surefilre-plugin</artifactId> <configuration> <groups>fast</groups> </configuration> </plugin> </plugins> </build> </profile> <profile> <id>ci</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <artifactId>maven-surefilre-plugin</artifactId> <configuration> <groups>fast | slow</groups> </configuration> </plugin> </plugins> </build> </profile> </profiles>
ㆍmaven설정으로 @Tag("fast")인 것만 빌드 시 실행
JUnit5 커스텀 태그
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) // 이 애노테이션의 전략은 런타임 @Test @Tag("fast") public @interface FastTest { } // ------------------------------- @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Test @Tag("slow") public @interface FastTest { }
JUnit5 테스트 반복하기
- @RepeatedTest
ㆍ반복 횟수와 반복 테스트 이름을 설정할 수 있다
º {displayName}
º {currentRepetition}
º {totalRepetitions}
ㆍRepetitionInfo 타입의 인자를 받을 수 있다
@DisplayName("스터디 만들기") @RepeatedTest(value = 10, name = "{displayName}, {currentRepetition}/{totalRepetitions}") void repeatTest(RepetitionInfo repetitionInfo) { System.out.println("test" + repetitionInfo.getCurrentRepetition() + "/" + repetitionInfo.getTotalRepetitions()); }
- @ParameterizedTest
ㆍ테스트에 여러 다른 매개변수를 대입해가며 반복 실행한다
º {displayName}
º {index}
º {argument}
º {0}, {1}, ...
@DisplayName("스터디 만들기") @ParameterizedTest(name = "{index} {displayName} message={0}") @ValueSource(strings = {"날씨가", "많이", "추워지고", "있네요."}) void parameterizedTest(String message){ System.out.println(message); }
ㆍ인자 값들의 소스
º @ValueSource
º @NullSource - null 인자 추가, @EmptySource - 비어있는 인자 추가, @NullAndEmptySource - 앞선 두개
º @EnumSource
º @MethodSource
º @CvsSource
º @CvsFileSource
º @ArgumentSource
ㆍ인자 값 타입 변환
º 암묵적인 타입 변환
@DisplayName("스터디 만들기") @ParameterizedTest(name = "{index} {displayName} message={0}") @ValueSource(ints = {10,20,40}) void parameterizedTest(Study study){ System.out.println(study); }
º 명시적인 타입 변환
- SimpleArgumentConverter 상속 받은 구현체 제공
- @ConvertWith
@DisplayName("스터디 만들기") @ParameterizedTest(name = "{index} {displayName} message={0}") @ValueSource(ints = {10,20,40}) void parameterizedTest(@ConvertWith(StudyConverter.class) Study study){ System.out.println(study); } 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())); } }
ㆍ인자 값 조합
º ArgumentsAccessor
º 커스텀 Accessor
- ArgumentsAggregator 인터페이스 구현
- @AggregateWith
// 방법1 @DisplayName("스터디 만들기") @ParameterizedTest(name = "{index} {displayName} message={0}") @CsvSource({"10, '자바 스터디'","20, '스프링'"}) void parameterizedTest2(Integer limit, String name){ Study study = new Study(limit, name); System.out.println(study); } // 방법2 @DisplayName("스터디 만들기") @ParameterizedTest(name = "{index} {displayName} message={0}") @CsvSource({"10, '자바 스터디'","20, '스프링'"}) void parameterizedTest2(ArgumentsAccessor argumentsAccessor){ Study study = new Study(argumentsAccessor.getInteger(0),argumentsAccessor.getString(1)); System.out.println(study); } // 방법3 @DisplayName("스터디 만들기") @ParameterizedTest(name = "{index} {displayName} message={0}") @CsvSource({"10, '자바 스터디'","20, '스프링'"}) void parameterizedTest2(@AggregateWith(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)); } }
JUnit5 테스트 인스턴스
- JUnit은 테스트 메소드마다 테스트 인스턴스를 새로 만든다
ㆍ이것이 기본 전략
ㆍ테스트 메소드를 독립적으로 실행하여 예상치 못한 부작용을 방지하기 위함
ㆍ이 전략을 JUnit 5에서 변경할 수 있다
- @TestInstance(Lifecycle.PER_CLASS)
ㆍ테스트 클래스당 인스턴스를 하나만 만들어 사용한다.
ㆍ경우에 따라, 테스트 간에 공유하는 모든 상태를 @BeforeEach 또는 @AfterEach에서 초기화할 필요가 있다
ㆍ@BeforeAll과 @AfterAll을 인스턴스 메소드 또는 인터페이스에 정의한 default 메소드로 정의할 수도 있다.
@TestInstance(TestInstance.Lifecycle.PER_CLASS) class StudyTest { ... }
JUnit5 테스트 순서
- 실행할 테스트 메소드 특정한 순서에 의해 실행되지만 어떻게 그 순서를 정하는지는 의도적으로 분명히 하지 않는다.
( 테스트 인스턴스를 테스트마다 새로 만드는 것과 같은 이유 )
- 경우에 따라, 특정 순서대로 테스트를 실행하고 싶을 때도 있다. 그 경우에는 테스트 메소드를 원하는 순서에 따라 실행하도록 @TestInstance(Lifycycle.PER_CLASS)와 함께 @TestMethodOrder를 사용할 수 있다
ㆍMethodOrderer 구현체를 설정한다
ㆍ기본 구현체
º Alphanumeric
º OrderAnnoation
º Random
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) class StudyTest { @Order(2) void test() {...} @Order(1) void test1() {...} ... }
JUnit5 : junit-platform.properties
- JUnit 설정 파일로, 클래스패스 루트(src/test/resources/)에 넣어두면 적용된다
- 테스트 인스턴스 라이프사이클 설정
ㆍjunit.jupiter.testinstance.lifecycle.default = per_class
- 확장팩 자동 감지 기능
ㆍjunit.jupiter.extensions.autodetection.enabled = true
- @Disabled 무시하고 실행하기
ㆍjunit.jupiter.conditions.deactivate = org.junit.*DisabledCondition
- 테스트 이름 표기 전략 설정
ㆍjunit.jupiter.displayname.generator.default = org.junit.jupiter.api.DisplayName.Generator$ReplaceUnderscores
JUnit5 : 확장 모델
- JUnit4의 확장 모델은 @RunWith(Runner), TestRule, MethodRule
- JUnit5의 확장 모델은 단 하나, Extension
- 확장팩 등록 방법
ㆍ선언적인 등록 @ExtendWith
@ExtendWith(FindSlowTestExtension.class)
ㆍ프로그래밍 등록 @RegisterExtension
class StudyTest { @RegisterExtension static FindSlowTestExtension findSlowTestExtension = new FindSlowTestExtension(1000L); ... }
ㆍ자동 등록 자바 ServiceLoader 이용
- 확장팩 만드는 방법
ㆍ테스트 실행 조건
ㆍ테스트 인스턴스 팩토리
ㆍ테스트 인스턴스 후-처리기
ㆍ테스트 매개변수 리졸버
ㆍ테스트 라이플사이클 콜백
ㆍ예외 처리
JUnit5 : 마이그레이션
- junit-vintage-engine을 의존성으로 추가하면, JUnit 5의 junit-platform으로 Junit 3과 4로 작성된 테스트를 실행할 수 있다
ㆍ@Rule은 기본적으로 지원하지 않지만, junit-jupiter-migrationsupport 모듈이 제공하는
@EnableRuleMigrationSupport를 사용하면 다음 타입의 Rule을 지원한다
º ExternalResource
º Verifier
º ExpectedException
JUnit 4 JUnit 5 @Category(Class) @Tag(String) @RunWith, @Rule, @ClassRule @ExtendWith, @RegisterExtension @Ignore @Disabled @Before, @After, @BeforeClass, @AfterClass @BeforeEach, @AfterEach, @BeforeAll, @AfterAll 'Test > 더 자바, 테스트하는 다양한 방법' 카테고리의 다른 글
2. Mockito (0) 2022.11.11