λ°μν
JUnit5 Annotation
λ보기
- ννκ³ μ ꡬκΈμ λμνμ§λ§ μ€μμ΄ μμ μ μμ΅λλ€. μμ΄ μλ¬Έλ κ°μ΄ λ΄μ£ΌμκΈΈ λ°λλλ€. βΊοΈ
@Test | Denotes that a method is a test method. Unlike JUnit 4’s @Test annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter operate based on their own dedicated annotations. Such methods are inherited unless they are overridden. | - ν΄λΉ λ©μλκ° ν
μ€νΈ λ©μλμμ λνλ. - JUnit 4μ λ¬λ¦¬ μμ± μ μΈ μμ |
@ParameterizedTest | Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden. | - λ©μλμ νλΌλ―Έν°λ₯Ό μ λ ₯ν΄ μ¬μ©ν μ μμ |
@RepeatedTest | Denotes that a method is a test template for a repeated test. Such methods are inherited unless they are overridden. | - λ°λ³΅ ν μ€νΈλ₯Ό ν μ μμ |
@TestFactory | Denotes that a method is a test factory for dynamic tests. Such methods are inherited unless they are overridden. | - λ€μ΄λλ―Ή ν
μ€νΈλ₯Ό νκΈ° μν ν
μ€νΈ ν©ν 리μ (μ νν μ©λ κ²μ νμ) |
@TestTemplate | Denotes that a method is a template for test cases designed to be invoked multiple times depending on the number of invocation contexts returned by the registered providers. Such methods are inherited unless they are overridden. | - (μ νν μ©λ κ²μ νμ) |
@TestClassOrder | Used to configure the test class execution order for @Nested test classes in the annotated test class. Such annotations are inherited. | - ν μ€νΈ ν΄λμ€ μμλ₯Ό μ§μ νλλ° μ¬μ©λ¨ |
@TestMethodOrder | Used to configure the test method execution order for the annotated test class; similar to JUnit 4’s @FixMethodOrder. Such annotations are inherited. | - ν μ€νΈ λ©μλ μμλ₯Ό μ§μ νλλ° μ¬μ©λ¨ |
@TestInstance | Used to configure the test instance lifecycle for the annotated test class. Such annotations are inherited. | - ν μ€νΈ κ°μ²΄ lifecycleμ μ§μ νλλ° μ¬μ©λ¨ |
@DisplayName | Declares a custom display name for the test class or test method. Such annotations are not inherited. | - ν μ€νΈ ν΄λμ€λ λ©μλμ μ΄λ¦μ λΆμ¬ν¨ |
@DisplayNameGeneration | Declares a custom display name generator for the test class. Such annotations are inherited. | - display name generator |
@BeforeEach | Denotes that the annotated method should be executed before each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @Before. Such methods are inherited – unless they are overridden or superseded (i.e., replaced based on signature only, irrespective of Java’s visibility rules). | - ν μ€νΈκ° μ€νλκΈ° μ μ μ€νλμ΄μΌ νλ λ©μλ |
@AfterEach | Denotes that the annotated method should be executed after each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @After. Such methods are inherited – unless they are overridden or superseded (i.e., replaced based on signature only, irrespective of Java’s visibility rules). | - ν μ€νΈκ° μ€νλ νμ μ€νλμ΄μΌ νλ λ©μλ |
@BeforeAll | Denotes that the annotated method should be executed before all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @BeforeClass. Such methods are inherited – unless they are hidden, overridden, or superseded, (i.e., replaced based on signature only, irrespective of Java’s visibility rules) – and must be static unless the "per-class" test instance lifecycle is used. | - ν΄λΉ ν μ€νΈ ν΄λμ€λ₯Ό μ΄κΈ°νν λ λ± νλ² μνλ¨ |
@AfterAll | Denotes that the annotated method should be executed after all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class; analogous to JUnit 4’s @AfterClass. Such methods are inherited – unless they are hidden, overridden, or superseded, (i.e., replaced based on signature only, irrespective of Java’s visibility rules) – and must be static unless the "per-class" test instance lifecycle is used. | - ν΄λΉ ν μ€νΈ ν΄λμ€λ₯Ό λ€ μ€νμν€κ³ λ± νλ² μνλ¨ |
@Nested | Denotes that the annotated class is a non-static nested test class. On Java 8 through Java 15, @BeforeAll and @AfterAll methods cannot be used directly in a @Nested test class unless the "per-class" test instance lifecycle is used. Beginning with Java 16, @BeforeAll and @AfterAll methods can be declared as static in a @Nested test class with either test instance lifecycle mode. Such annotations are not inherited. | - ν μ€νΈ κ·Έλ£Ή κ°μ κ³μΈ΅μ ννν μ μλ€ |
@Tag | Used to declare tags for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level. | - ν μ€νΈ ν΄λμ€λ λ©μλμ μ¬μ©ν΄ ν μ€νΈλ€μ νν°λ§νλλ° μ¬μ©λλ€. |
@Disabled | Used to disable a test class or test method; analogous to JUnit 4’s @Ignore. Such annotations are not inherited. | - ν μ€νΈλ₯Ό λΉνμ±ννλ€. |
@Timeout | Used to fail a test, test factory, test template, or lifecycle method if its execution exceeds a given duration. Such annotations are inherited. | - μ§μ λ μκ°μ μ΄κ³Όνλ κ²½μ° ν μ€νΈ μ€ν¨λ‘ μ€μ νλ€ |
@ExtendWith | Used to register extensions declaratively. Such annotations are inherited. | - (μ νν μ©λ κ²μ νμ) |
@RegisterExtension | Used to register extensions programmatically via fields. Such fields are inherited unless they are shadowed. | - (μ νν μ©λ κ²μ νμ) |
@TempDir | Used to supply a temporary directory via field injection or parameter injection in a lifecycle method or test method; located in the org.junit.jupiter.api.io package. | - μμ λλ ν 리λ₯Ό μ 곡νλλ° μ¬μ©λλ€. |
@ParameterizedTest
- μ¬λ¬ 맀κ°λ³μλ₯Ό μ λ ₯ λ°μ ν μ€νΈλ₯Ό ν μ μλ€(λ€κ±΄ μ 맀κ°λ³μ κ°μλ§νΌ ν μ€νΈ λ°λ³΅ λ¨)
- ValueSource μ ν¨κ» μ¬μ©λλ©°, ValueSource μ ν μ€νΈμ μ¬μ©ν 맀κ°λ³μλ₯Ό μ λ ₯νλ©΄ λλ€.
@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
assertTrue(StringUtils.isPalindrome(candidate));
}
// κ²°κ³Ό
palindromes(String) β
ββ [1] candidate=racecar β
ββ [2] candidate=radar β
ββ [3] candidate=able was I ere I saw elba β
@RepeatedTest
- λ°λ³΅ νμλ₯Ό μ§μ νκ³ , μ§μ λ νμλ§νΌ ν μ€νΈλ₯Ό λ°λ³΅νλ€.
@RepeatedTest(10)
void repeatedTest() {
// ...
}
- name μμ±μ μ΄μ κ°μ κ°λ€μ μ΄μ©ν΄ μ 보λ₯Ό μΆλ ₯ν μ μλ€.
- {displayName}: @RepeatedTest λ©μλ μ νμ μ΄λ¦
- {currentRepetition}: νμ¬ λ°λ³΅ νμ
- {totalRepetitions}: μ΄ λ°λ³΅ νμ
- RepetitionInfo λ‘ νμ¬ μ€ν μ€μΈ λ°λ³΅ ν μ€νΈμ λν μ 보λ₯Ό μ»μ μ μλ€.
@BeforeEach
void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) {
int currentRepetition = repetitionInfo.getCurrentRepetition();
int totalRepetitions = repetitionInfo.getTotalRepetitions();
String methodName = testInfo.getTestMethod().get().getName();
logger.info(String.format("About to execute repetition %d of %d for %s", //
currentRepetition, totalRepetitions, methodName));
}
@RepeatedTest(10)
void repeatedTest() {
// ...
}
@RepeatedTest(5)
void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) {
assertEquals(5, repetitionInfo.getTotalRepetitions());
}
@RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}")
@DisplayName("Repeat!")
void customDisplayName(TestInfo testInfo) {
assertEquals("Repeat! 1/1", testInfo.getDisplayName());
}
@RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME)
// RepeatedTest.LONG_DISPLAY_NAMEμ²λΌ 미리 μ μ λ ν¨ν΄μ΄ μ‘΄μ¬νλ€.
@DisplayName("Details...")
void customDisplayNameWithLongPattern(TestInfo testInfo) {
assertEquals("Details... :: repetition 1 of 1", testInfo.getDisplayName());
}
@RepeatedTest(value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}")
void repeatedTestInGerman() {
// ...
}
// κ²°κ³Ό
ββ RepeatedTestsDemo β
β ββ repeatedTest() β
β β ββ repetition 1 of 10 β
β β ββ repetition 2 of 10 β
β β ββ repetition 3 of 10 β
β β ββ repetition 4 of 10 β
β β ββ repetition 5 of 10 β
β β ββ repetition 6 of 10 β
β β ββ repetition 7 of 10 β
β β ββ repetition 8 of 10 β
β β ββ repetition 9 of 10 β
β β ββ repetition 10 of 10 β
β ββ repeatedTestWithRepetitionInfo(RepetitionInfo) β
β β ββ repetition 1 of 5 β
β β ββ repetition 2 of 5 β
β β ββ repetition 3 of 5 β
β β ββ repetition 4 of 5 β
β β ββ repetition 5 of 5 β
β ββ Repeat! β
β β ββ Repeat! 1/1 β
β ββ Details... β
β β ββ Details... :: repetition 1 of 1 β
β ββ repeatedTestInGerman() β
β ββ Wiederholung 1 von 5 β
β ββ Wiederholung 2 von 5 β
β ββ Wiederholung 3 von 5 β
β ββ Wiederholung 4 von 5 β
β ββ Wiederholung 5 von 5 β
@TestClassOrder
- ν μ€νΈ ν΄λμ€λ μΌλ°μ μΌλ‘ μ€ν μμμ μμ‘΄μ μ΄λ©΄ μλμ§λ§, νΉμ ν μ€νΈ ν΄λμ€ μ€ν μμλ₯Ό μ μ©νλ κ²μ΄ νμν λλ μλ€. (ν΄λμ€κ° μμ μ§μ )
- @TestClassOrder μμ ClassOrdererꡬν체λ₯Ό μ§μ νλ©΄ λλ€.
ClassOrderer.ClassName | Classλͺ μμΌλ‘ μ λ ¬ |
ClassOrderer.DisplayName | DisplayName μμΌλ‘ μ λ ¬ |
ClassOrderer.OrderAnnotation | @Order μμΌλ‘ μ λ ¬ |
ClassOrderer.Random | λλ€ |
@TestClassOrder(ClassOrderer.OrderAnnotation.class) // @Order μ λ ¬
class OrderedNestedTestClassesDemo {
@Nested
@Order(1)
class PrimaryTests {
@Test
void test1() {
}
}
@Nested
@Order(2)
class SecondaryTests {
@Test
void test2() {
}
}
}
@TestMethodOrder
- ν μ€νΈ λ©μλλ μΌλ°μ μΌλ‘ μ€ν μμμ μμ‘΄μ μ΄λ©΄ μλμ§λ§, νΉμ ν μ€νΈ λ©μλ μ€ν μμλ₯Ό μ μ©νλ κ²μ΄ νμν λλ μλ€. (λ©μλ μμ μ§μ )
- @TestMethodOrder μμ MethodOrderer ꡬν체λ₯Ό μ§μ νλ©΄ λλ€.
MethodOrderer.ClassName | Classλͺ μμΌλ‘ μ λ ¬ |
MethodOrderer.DisplayName | DisplayName μμΌλ‘ μ λ ¬ |
MethodOrderer.OrderAnnotation | @Order μμΌλ‘ μ λ ¬ |
MethodOrderer.Random | λλ€ |
@TestMethodOrder(OrderAnnotation.class) // @Order μ λ ¬
class OrderedTestsDemo {
@Test
@Order(1)
void nullValues() {
// perform assertions against null values
}
@Test
@Order(2)
void emptyValues() {
// perform assertions against empty values
}
@Test
@Order(3)
void validValues() {
// perform assertions against valid values
}
}
@TestInstance
- κ° ν μ€νΈ λ©μλλ₯Ό λ 립μ μΌλ‘ μ€ννκ³ , ν μ€νΈ μΈμ€ν΄μ€μ λ³κ²½μΌλ‘ μΈν΄ μκΈ°μΉ μλ λΆμμ©μ νΌνκΈ° μν΄ JUnitμ κ° ν μ€νΈ λ©μλλ₯Ό μ€ννκΈ° μ μ μλ‘κ² μΈμ€ν΄μ€λ₯Ό λ§λ λ€.
- λμΌν ν μ€νΈ μΈμ€ν΄μ€μμ λͺ¨λ ν μ€νΈ λ©μλκ° μ€νλκ² νλ €λ©΄ @TestInstance(Lifecycle.PER_CLASS) μ΄λ Έν μ΄μ μ μ¬μ©νλ€.
- μ°Έκ³ λ‘ JUnit ν
μ€νΈ μΈμ€ν΄μ€ μμ±κ³Ό κ΄λ ¨ν κΈ°λ³Έ μ€μ μ PER_METHOD μ΄λ€.
- = λ©μλλ§λ€ μλ‘κ² μμ±
@DisplayName
- ν
μ€νΈ μ€νκΈ° λ° IDEμ μν΄ νμλλ μ¬μ©μ μ§μ νμ μ΄λ¦μ μ μΈ
- 곡백, νΉμλ¬Έμ, μ΄λͺ¨μ§λ μ¬μ©ν μ μλ€.
@DisplayName("A special test case")
class DisplayNameDemo {
@Test
@DisplayName("Custom test name containing spaces")
void testWithDisplayNameContainingSpaces() {
}
@Test
@DisplayName("β―°β‘°οΌβ―")
void testWithDisplayNameContainingSpecialCharacters() {
}
@Test
@DisplayName("π±")
void testWithDisplayNameContainingEmoji() {
}
}
@Nested
- μ¬λ¬ ν μ€νΈ κ·Έλ£Ή κ°μ κ³μΈ΅μ ννν μ μλ€.
@DisplayName("A stack")
class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
@Tag
- ν μ€νΈ ν΄λμ€ λ° λ©μλμ μ¬μ©ν΄ ν μ€νΈ κ²μ λ° μ€ν μ νν°λ§νλ λ° μ¬μ©ν μ μλ€.
@Tag("fast")
@Tag("model")
class TaggingDemo {
@Test
@Tag("taxes")
void testingTaxCalculation() {
}
}
- νν°λ§μ νλ λ°©λ²μ μ°¨μ°¨ μμ보μ. π§
@Disabled
- μ 체 ν μ€νΈ ν΄λμ€ λλ ν μ€νΈ λ©μλ, μ‘°κ±΄λΆ μ€ν ν μ€νΈλ₯Ό λΉνμ±ν ν μ μλ€.
@Disabled("Disabled until bug #99 has been fixed")
class DisabledClassDemo {
@Test
void testWillBeSkipped() {
}
}
class DisabledTestsDemo {
@Disabled("Disabled until bug #42 has been resolved")
@Test
void testWillBeSkipped() {
}
@Test
void testWillBeExecuted() {
}
}
- @Disable(μ΄μ ) : μ΄μ²λΌ μ΄λ Έν μ΄μ μμ ν μ€νΈκ° λΉνμ±ν λ μ΄μ λ₯Ό μμ±ν μλ μκ³ , μλ΅ν μλ μλλ° JUnit νμ λΉνμ±ν λ μ΄μ λ₯Ό μ λ κ²μ κΆμ₯ν¨.
@Timeout
- μ§μ λ μκ°μ μ΄κ³Όνλ κ²½μ° ν μ€νΈ μ€ν¨λ‘ μ€μ
class TimeoutDemo {
@BeforeEach
@Timeout(5) // κΈ°λ³Έ μ€μ μ μ΄λ¨μμ΄λ€.
void setUp() {
// fails if execution time exceeds 5 seconds
}
@Test
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
void failsIfExecutionTimeExceeds500Milliseconds() {
// fails if execution time exceeds 500 milliseconds
}
@Test
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS, threadMode = ThreadMode.SEPARATE_THREAD)
void failsIfExecutionTimeExceeds500MillisecondsInSeparateThread() {
// fails if execution time exceeds 500 milliseconds, the test code is executed in a separate thread
}
}
@TempDir
- μ€νμ κΈ°λ₯
- μμ λλ ν 리λ₯Ό μ 곡νλ€.
@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("test.txt");
new ListWriter(file).write("a", "b", "c");
assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}
// μ¬λ¬κ°κ° νμν κ²½μ°
@Test
void copyFileFromSourceToTarget(@TempDir Path source, @TempDir Path target) throws IOException {
Path sourceFile = source.resolve("test.txt");
new ListWriter(sourceFile).write("a", "b", "c");
Path targetFile = Files.copy(sourceFile, target.resolve("test.txt"));
assertNotEquals(sourceFile, targetFile);
assertEquals(singletonList("a,b,c"), Files.readAllLines(targetFile));
}
// λλ ν 리 μμ μ¬λΆ(μ±κ³΅μμλ§ μμ )
@Test
void fileTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) {
// perform test
}
- ν
μ€νΈ μλ£ ν μμ λλ ν 리 μμ μ¬λΆλ₯Ό μ§μ ν μ μλ€.
- The @TempDirannotation has an optional cleanup attribute that can be set to either NEVER, ON_SUCCESS, or ALWAYS
πβοΈ μ°Έκ³
spring-boot-starter-test
λ λ€μκ³Ό κ°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό ν¬ν¨νκ³ μλ€JUnit 5
Spring Test & Spring Boot Test
Assertj
Hamcrest
Mockito
JSONassert
JsonPath
π Reference
λ°μν