CH01 JUnit 5
๊ตฌ๋์๋ฆฌ
๋งค๋ฒ ํ ์คํธ ์ฝ๋ ์ง๋๋ฐ์ ๊ณต์ ๋ง์ด ๋ค์ด๊ธด ํ๋๋ฐ, ์ ์ ํ ์คํธ๊ฐ ๋์ ํ๋ ์๋ฆฌ์ ๋ํด์๋ ๊ด์ฌ์ ๋์ง ์์๋ ๊ฒ ๊ฐ๋ค.

IDE๊ฐ JUnit Platform Launcher๋ฅผ ๋ก๋ํ๊ณ JUnit Platform Launcher๊ฐ JUnit Platform์ ํ ์คํธ ์์ง์ ๋ก๋ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํ ์คํธ ์์ง์ด ํ ์คํธ ์ฝ๋๋ฅผ ์คํํ๋๋ฐ ์ด ์ง์ ์ ์ปดํฌ๋ํธ ์ค์บ์ด ์ํ๋๋ค.
์์์ ์ ๋ฆฌ๋ฅผ ํ์๋ฉด ์๋์ ๊ฐ๋ค.
IDE๊ฐ JUnit Platform Launcher๋ฅผ ๋ก๋
JUnit Platform Launcher๊ฐ JUnit Platform์ ํ ์คํธ ์์ง์ ๋ก๋
ํด๋์ค ๋ก๋ฉ ๋ฐ ์ปดํฌ๋ํธ ์ค์บ
ํ ์คํธ ์ฝ๋ ์คํ
์ ๊ทธ๋ฆผ์ JUnit ์ ์ฒด๋ฅผ ๊ฐ๋ตํ๊ฒ ๋์ํ ํ ๊ฒ์ด๊ณ Jupiter๋ JUnit 5 ์ ๊ตฌํ์ฒด API ๋ผ๊ณ ์ดํดํ๋ฉด ๋๋ค.
๊ธฐ๋ณธ์ ์ธ annotation ์ ๋ฆฌ (JUnit 5 ๊ธฐ์ค)
NEXTSTEP ์์ ์์ฃผ ๊ฐํ๊ฒ ๊ฐ์กฐํ๋ ๊ฒ์ด ํ ์คํธ ์ฝ๋ ์์ฑ์ด๊ณ ๋ค์๋ ๋๋ถ๋ถ์ ๊ฐ์์์ ํ ์คํธ ์ฝ๋์ ๋ค์ํ ํ์ฉ์ ๋ํด์ ํผ๋๋ฐฑ์ ๋ฐ์๋ค. ์ง๊ธ ์ด ๊ฐ์๋ ์ฌ์ค ์๊ฐ์ ๊ฝค ์ค๋์ ์ ํ๊ณ ๋ค์ ์ ๋ฆฌ๋ฅผ ํ๋ ์ฐจ์์์ ๋ณด๊ณ ์๋๋ฐ, ๋น์์๋ ๋ชฐ๋์ผ๋ ์ง๊ธ ๋ณด๋ NEXTSTEP ์์ ํผ๋๋ฐฑ ํด์ฃผ๋ ์์ค์ ๋์ ๋ํ ์ผ์ ๊ฐ์์์ ๋ค๋ฃจ๊ณ ์๋ ๊ฒ ๊ฐ๋ค. ๊ฐ์์ ํ๋ฆฌํฐ๊ฐ ์ข์ ๊ฒ ๊ฐ๋ค๋ ์๋ฏธ์ด๋ค.
์๋๋ ๊ณผ๊ฑฐ์ ๊ฐ์๋ฅผ ๋ค์ ๋ ์ ๋ฆฌํ ๋ ธํธ์ด๋ค.
@BeforeAll / @AfterAll
ํด๋น ๋ฉ์๋๊ฐ ์ ์ธ๋ ํด๋์ค์ ๋ชจ๋ @Test ๋ค์ด ์์๋๊ธฐ ์ ๊ณผ ํ์ ํ ๋ฒ์ฉ ์คํ๋๋ฉฐ static ์ผ๋ก ๋ง๋ค์ด ์ค์ผํจ. ์๋ํ๋ฉด ๋์ค์ ๋ค๋ฃจ๊ฒ ์ง๋ง @TestInstance์ ๋ผ์ดํ์ฌ์ดํด์ด ๊ฐ๊ฐ์ @Test ๋ง๋ค ์๋ก ๋ง๋ค๊ธฐ ๋๋ฌธ์ ๊ฐ๊ฐ์ ์ธ์คํด์ค์์ @BeforeAll / @AfterAll๋ก ๋ง๋ค์ด์ค ๋ฉ์๋์ ์ ๊ทผํ๋ ค๋ฉด static์ด ํ์ํ๋ค.
๋ฐ๋๋ก @TestInstance์ ๋ผ์ดํ์ฌ์ดํด์ ํด๋์ค๋ก ๋ณ๊ฒฝํ๋ฉด ํ๋์ ํด๋์ค์์ ์ฌ๋ฌ ํ ์คํธ๋ค์ด ๋์ํ๊ธฐ ๋๋ฌธ์ static ์ผ๋ก ํด์ค ํ์๊ฐ ์์ด์ง๋ค.
@TestInstance(TestInstance.Lifecycle.PER_METHOD) // default
//@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@BeforeEach / @AfterEach
ํด๋น ๋ฉ์๋๊ฐ ์ ์ธ๋ ํด๋์ค์ ๋ชจ๋ @Test ๊ฐ๊ฐ์ ์ ๊ณผ ํ์ ์คํ(@Test ๋ฉ์๋ ์๋งํผ ์ ๊ณผ ํ์ ์คํ๋จ)
@Disabled
์ด ์ด๋ ธํ ์ด์ ์ด ๋ณ๊ฑฐ ์๋๋ฐ ํ๊ธฐ์ ์ด๋ค. ์๋๋ฉด ๋๋ ํ ์คํธ์ฉ์ผ๋ก ์ด๊ฑธ ์ฃผ์์ฒ๋ฆฌํด์ ๋๋ฆฌ๊ณ , ์ฃผ์ ํ๊ณ ๋ค์ ๋๋ฆฌ๊ณ ํ์๊ธฐ ๋๋ฌธ์ด๋ค. ํ๋ก๋์ ์ฝ๋์ ์จ๋จน๋ ๊ฒฝ์ฐ๋ ๋ง์ผ์ ์ํด์ ํ ์คํธ ์ฝ๋๋ฅผ ๋จ๊ฒจ๋๊ณ ์ถ์ง๋ง ์ฃผ์์ฒ๋ฆฌ๋ก ํด๋๊ธฐ ์ซ์ผ๋ฉด ์จ๋จน์ผ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.
ํ
์คํธ ์ด๋ฆ ํ๊ธฐํ๊ธฐ
@DisplayNameGeneration
ํด๋น ํด๋์ค ์ ์ฒด์ @Test์ ๋ํ ์ด๋ฆ์ ํ๊ธฐํ๋ ์ ๋ต์ ์ค์ ํ ์ ์์
@DisplayName
๊ฐ๋ณ @Test์ ์ด๋ฆ์ ์ค์ (@DisplayNameGeneration๋ณด๋ค ์ฐ์ ์์ ๋์)
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
}
JUnit 5 Assertion
๊ฐ์ธ์ ์ผ๋ก ํ ์คํธ ์ฝ๋์ ๋ํ ์ต๊ด ์์ฒด๊ฐ NEXTSTEP ๊ฐ์๋ฅผ ๋ค์ผ๋ฉด์ ๋ง์ด ํ์ฑ์ด ๋์๋๋ฐ, NEXTSTEP์์ org.assertj.core.api.Assertions ์ ๋ง์ด ํ์ฉํ๋๋ก ๊ฐ์ด๋ ํด์ค์ jupiter ๊ฒ์ ๊ฐ์ธ์ ์ผ๋ก ์ ์์ด๋ค. ์ด๊ฑด ํ ์ปจ๋ฒค์ ์ ๋ฐ๋ผ๊ฐ๋ฉด ์ข์ ๋ฏ ํ๊ณ , ์๋๋ ์์ ์ ์ ๋ฆฌํด๋ ๋ ธํธ๋ฅผ ๊ทธ๋๋ก ๋จ๊ธด๋ค.
assertEquals(expected, actual) : ์ค์ ๊ฐ์ด ๊ธฐ๋ํ ๊ฐ๊ณผ ๊ฐ์์ง ํ์ธ(ํ๋ผ๋ฏธํฐ ์์๊ฐ ๋ฌด๊ดํ๊ธด ํ์ง๋ง ์ข์ธก์ด ๊ธฐ๋๊ฐ, ์ฐ์ธก์ด ์ถ๋ ฅ๊ฐ์์ ์๊ณ ์ฐ์)
assertNotNull(actual) : ์ถ๋ ฅ๊ฐ์ด null์ด ์๋์ง ํ์ธ
assertTrue(boolean) : ๋ค์ ์กฐ๊ฑด์ด ์ฐธ์ธ์ง ํ์ธ
assertAll(executablesโฆ) : ํ๋์ @Test์์ ๋ ๊ฐ ์ด์์ assert๋ฌธ์ ์ฌ์ฉํ์ ๋ ์ฒซ๋ฒ์งธ assert์์ fail์ด ๋ ๊ฒฝ์ฐ ๋๋ฒ์งธ assert๋ฅผ ์คํํ์ง ์์. ์ด ๋ ๋ assert๋ฅผ assertAll๋ก ๊ฐ์ธ์ฃผ๋ฉด ๋๋ค ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
assertThrows(expectedType, executable) : ์์ธ ๋ฐ์ ํ์ธ
assertTimeout(duration, executable) : ํน์ ์๊ฐ ์์ ์คํ์ด ์๋ฃ๋๋์ง ํ์ธ
@Test
@DisplayName("์คํฐ๋ ๋ง๋ค๊ธฐ")
void createNewStudy() {
Study study = Study.builder()
.studyStatus(StudyStatus.DRAFT)
.limit(20)
.build();
assumeTrue(study != null);
assertAll(
() -> assertEquals(StudyStatus.DRAFT, study.getStudyStatus(), "์คํฐ๋๋ฅผ ์ฒ์ ๋ง๋ค๋ฉด ์ํ๊ฐ์ด DRAFT ์ฌ์ผ ํ๋ค."),
() -> assertTrue(study.getLimit() > 10, "์คํฐ๋์ ์ต์ ์ธ์์ 10๋ช
์ ์ด๊ณผํด์ผ ํ๋ค.")
);
}
ํ ์คํธ ์์ฒด๊ฐ ํ๋๋ผ๋ ํต๊ณผํ์ง ๋ชปํ๋ฉด ๋ฌธ์ ๊ฐ ์๊ธฐ ๋๋ฌธ์ assertAll์ ์ฌ์ฉํ์ง ์์๋ ๊ฒฐ๊ตญ ํ ์คํธ๊ฐ ํต๊ณผํ์ง ๋ชปํ๋ฉด ํด๋น @Test ์์ฒด๊ฐ ์คํจํ ๊ฒ์ผ๋ก ์ธ์ํ ์ ์์ด์ ๋ฌธ์ ๋ ๊ฒ์ ์๋ค.
ํ์ง๋ง ํ๋์ @Test ๋ด์ ๋ ๊ฐ ์ด์์ assert ๋ฌธ์ด ์๊ณ , ํ ์คํธ๋ฅผ ํ์ ๋ ์ด๋ค ๊ฒ์ ์คํจํ๊ณ ์ด๋ค ๊ฒ์ ํต๊ณผํ๋์ง๋ฅผ ๊ตฌ๋ถํ์ฌ ์ ์ ์๋ค๋ฉด ๊ฐ๋ฐ ์์ฒด๊ฐ(=ํ ์คํธ ์์ฒด๊ฐ) ๋ ์ฉ์ดํ ์ ์๊ธฐ ๋๋ฌธ์ assertAll์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
@Test
void statusTest() {
Study study = new Study();
assertEquals(StudyStatus.STARTED, study.getStudyStatus(), "์ฒ์ ์คํฐ๋๋ฅผ ๋ง๋ค๋ฉด ์ํ๋ DRAFT ์ฌ์ผ ํ๋ค");
assertEquals(StudyStatus.STARTED, study.getStudyStatus(), () -> "์ฒ์ ์คํฐ๋๋ฅผ ๋ง๋ค๋ฉด ์ํ๋ DRAFT ์ฌ์ผ ํ๋ค");
}
๋ฉ์ธ์ง๋ฅผ ๊ทธ๋ฅ ๊ทธ๋๋ก ๋ฃ์ด์ฃผ๋ ๊ฒ๊ณผ ๋๋ค์์ผ๋ก ๋ฃ์ด์ฃผ๋ ๋ฐฉ๋ฒ ๋๋ค ๋ฉ์ธ์ง ์ฒ๋ฆฌ์๋ ๋ฌธ์ ๊ฐ ์๋๋ฐ, ๋๋ค์์ผ๋ก ํ ๊ฒฝ์ฐ ํ ์คํธ๊ฐ ์คํจํ์ ๋์๋ง ๋ฉ์ธ์ง ์ฐ์ฐ์ ์ฒ๋ฆฌํ๋ค. ๊ทธ๋์ ๋ง์ฝ ์ฒ๋ฆฌํ ๋ฉ์ธ์ง ์ฐ์ฐ์ ์ฒ๋ฆฌ๋น์ฉ์ด ๋ถ๋ด์ด ๋ ์์ค์ด๋ผ๋ฉด ๋๋ค์์ผ๋ก ๋ฃ์ด์ฃผ๋ ๊ฒ์ด ์ข๋ค. ๊ทธ๋์ ๊ฒฐ๋ก ์ ์ผ๋ก๋ ๋ชจ๋ ๋ฉ์ธ์ง๋ฅผ ์ ๋ ๊ฒ ๋๋ค์์ผ๋ก ๋ง๋ค์ด์ฃผ๋ ์ต๊ด์ ๋ค์ด๋ ๊ฒ์ด ์ข๊ฒ ๋ค.
public Study(int limit) {
if (limit < 0) {
throw new IllegalArgumentException(Constant.studyLimitErrorMessage);
}
this.limit = limit;
}
@Test
void limitTest() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
Study study = new Study(-10);
});
assertEquals(exception.getMessage(), Constant.studyLimitErrorMessage);
}
assertThrows๋ฅผ ๋จ์ํ ์ํ๋ ์๋ฌ ํ์ ์ ๊ฐ์ง์๋ง ์ฌ์ฉํ๋ ๊ฒ ์ด์์ผ๋ก ์๋ฌ ๋ฉ์ธ์ง์ ์ผ์น์ฑ ์ฌ๋ถ๊น์ง ๋ฝ์์ ์ฌ์ฉํ ์ ์๋ค. ๋์ผํ ์๋ฌ ์ ํ ๋ด์์ ์กฐ๊ฑด์ ๋ฐ๋ผ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๋ค์ํ๊ธฐ ๋ถ๊ธฐํ๋ ๊ฒฝ์ฐ ์ฌ์ฉํ ์ ์๊ฒ ๋ค.
JUnit 5 ํ๊น
๊ณผ ํํฐ๋ง
@Tag
ํ ์คํธ ๊ทธ๋ฃน์ ๋ง๋ค๊ณ ์ํ๋ ํ ์คํธ ๊ทธ๋ฃน๋ง ํ ์คํธ๋ฅผ ์คํํ ์ ์๋ ๊ธฐ๋ฅ. intellij์ test configuration ์กฐ์(test kind = Tags, Tag expression = custom tag)์ ํตํด์ ํน์ ํ๊ทธ๋ง ํ ์คํธ ๋๋๋ก ํ๋ค.
ํํฐ๋ง
profile ๋ณ๋ก ํน์ ํ๊ทธ๋ง ๋ฌ๋ ค์๋ @Test ๋ง ์คํ๋๋๋ก ํ๋ ๋ฐฉ๋ฒ. ๊ฐ์์์๋ maven-surefile-plugin ์ ๋ค๋ค๊ณ gradle์ ์ฐพ์๋ณด์์ผ ํ๋ค. ์ด๋ฅผ ์ด์ฉํด ๋ฐฐํฌ ํ๊ฒฝ์์ ๋น๋์ ํน์ ํ๊ทธ์ ๋ํ ํ ์คํธ๋ฅผ ์ค์ ํด๋๊ณ ํ๋๋ผ๋ ์คํจํ๋ค๋ฉด ์ด๋ฅผ ๋ฐฐํฌํ์ง ์๋๋ก ํ๋ ์ ๋ต์ ์ฌ์ฉํ ์ ์๋ค.
์ปค์คํ
ํ๊ทธ
์ปค์คํ ํ๊ทธ๋ฅผ ์ฌ์ฉํ๋ฉด @Test์ ๋ถ์ฌํด์ค ์ฌ๋ฌ๊ฐ์ง ์ค์ ๋ค์ ๋จ์ํํ๊ณ ์ค๋ณต์ ์ ๊ฑฐํ์ฌ ์ ์ฉ์ํฌ ์ ์๋ค. ํนํ @Tag๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค๋ฉด ์ปค์คํ ํ๊ทธ๋ฅผ ์ด์ฉํ์ฌ type safeํ๊ฒ @Tag๋ฅผ ์ฌ์ฉํ ์ ์๋ค๋ ์ด์ ์ด ์๋ค. ๋ค์์ @Tag์ ๋งค๋ฒ ์ํ๋ tag๋ฅผ ๋ฌธ์์ด๋ก ์ ๋ ฅํ๋ ๊ฒ ๋ณด๋ค ์ค์ ํด๋ tag๊ฐ ์ ์ฉ๋ ์ปค์คํ ํ๊ทธ๋ฅผ ์ผ๊ด๋ก ์ ์ฉํ๋ ๊ฒ์ด ๊ณง type safeํ ๋ฐฉ๋ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@Tag("development")
public @interface DevelopmentTest {
}
JUnit 5: ํ
์คํธ ๋ฐ๋ณตํ๊ธฐ
@RepeatedTest
ํน์ ํ์๋งํผ ํ ์คํธ๋ฅผ ๋ฐ๋ณตํ์ฌ ํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ค. ํ ์คํธ ์ฝ์์ฐฝ์ ๋์ค๋ ํ ์คํธ๋ช ๊ณผ ๋ฐ๋ณต ํ์ ๋ฑ์ ์ปค์คํ ํ๊ฒ ๋ณ๊ฒฝํ ์ ์๋ค.
@RepeatedTest(value = 10, name = "{displayName}, {currentRepetition} / {totalRepetitions}")
@DisplayName("์คํฐ๋ ๋ฐ๋ณต ํ
์คํธ")
void repeatedTest(RepetitionInfo repetitionInfo) {
System.out.println(repetitionInfo.getCurrentRepetition() + " / " + repetitionInfo.getTotalRepetitions());
}
๋ง์ฝ placeholder ๋ค์ด ์ ๋๋ก ์๋ํ์ง ์๋๋ค๋ฉด Build, Execution, Deployment -> Build Tools -> Gradle๋ก ์ด๋ํ ๋ค์ Run tests using ์ Gradle -> Intellij IDE ๋ก ์์ ํ๋ฉด ๋๋ค.
@ParameterizedTest
@ValueSource๋ฅผ ์ด์ฉํด์ ํ๋์ @Test์ ์ฌ๋ฌ ๋ค๋ฅธ ๋งค๊ฐ๋ณ์(์ํ๋ ๋๋ก ์ ์ธ๋)๋ฅผ ๋์ ํด๊ฐ๋ฉฐ ๋ฐ๋ณต ์คํํ๋ค.
์ถ๊ฐ์ ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ ์ต์
@NullSource
@EmptySource
@NullAndEmptySource
@DisplayName("parameterized Test")
@ParameterizedTest(name = "{displayName} : {index} // {0}")
@ValueSource(strings = {"first", "second", "third", "fourth", "fifth"})
@NullSource
@EmptySource
//@NullAndEmptySource
void parametersTest(String param) {
System.out.println(param);
}
@ConvertWith + @ParameterizedTest
์ปค์คํ ํ ์ปจ๋ฒํฐ๋ฅผ ์ด์ฉํ ํ ์คํธ๋ฅผ ํ๊ณ ์ ํ ๋ ์ฌ์ฉ. ๋ฐ์ ์ธ์๊ฐ์ผ๋ก ๋ฐ๋ก ์ํ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ์ฌ์ฉํ ์ ์๋ค.(๋ฐ์์ ๋ด๋ถ์์ ๋ง๋ค์ง ์๊ณ ๋ฐ๋ก ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์) ํด๋น ์์ ๋ SimpleArgumentConverter๋ฅผ ์ฌ์ฉํ ์์ ๋ก 2๊ฐ ์ด์์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค.
@ParameterizedTest
@ValueSource(ints = {20, 30, 40, 50})
void converterTest(@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 Study");
return Study.builder().limit(Integer.parseInt(source.toString())).build();
}
}
@CsvSource + @AggregateWith + @ParameterizedTest
๋ณต์์ ํ๋ผ๋ฏธํฐ์ ์ด๋ฅผ ๋ฐ๋ก ๊ฐ์ฒด๋ก ๋ฐ์ ์ฌ์ฉํ๊ณ ์ถ์๋ ์ฌ์ฉ
@ParameterizedTest
@CsvSource({"10, ์๋ฐ", "20, ์คํ๋ง"})
void aggregateTest(@AggregateWith(StudyAggregator.class) Study study) {
System.out.println("limit : " + study.getLimit());
System.out.println("name : " + study.getName());
}
static class StudyAggregator implements ArgumentsAggregator {
@Override
public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) throws ArgumentsAggregationException {
return Study.builder().limit(accessor.getInteger(0)).name(accessor.getString(1)).build();
}
}
๊ทธ ์ธ ๊ฐ์ ์๋ฃ์ ์๊ฐ๋ ๊ฒ๋ค(๋ค์ํ๊ฒ ์ ์ฌ์ ์์ ์ ์จ๋จน์)
@ValueSource
@NullSource, @EmptySource, @NullAndEmptySource
@EnumSource
@MethodSource
@CsvSource
@CvsFileSource
@ArgumentSource
JUnit 5 ํ
์คํธ ์ธ์คํด์ค
JUnit์ ๊ธฐ๋ณธ์ ์ผ๋ก @Test ๋ง๋ค ํด๋น @Test๊ฐ ์ํ ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์๋ก ๋ง๋ ๋ค. ํ๋์ ํ ์คํธ ํด๋์ค์ ๋ ๊ฐ์ @Test ๊ฐ ์์ผ๋ฉด ํ ์คํธ ํด๋์ค ์ ์ฒด๋ฅผ ํ ์คํธ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ํด๋์ค๊ฐ ๋ ๊ฐ ์๊ธด๋ค๋ ๊ฒ์ด๋ค.
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
์ด๊ฒ์ด ๊ธฐ๋ณธ ๊ฐ์ด๋ค. ํ ์คํธ ์ธ์คํด์ค์ ๋ผ์ดํ ์ฌ์ดํด์ด '๋ฉ์๋ ๋ง๋ค' ๋ก ์ ํด์ง๋ค. (๋น์ฐํ ๋ณ๊ฒฝํ ์ ์๋ค) ์๋ ์ฝ๋๋ฅผ ๋ณด์.
package com.example.securitysample.sample;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_METHOD) // default
//@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class SampleTest {
int value = 0;
User user = new User();
@Test
void test1() {
value++;
System.out.println(value);
System.out.println(user);
Assertions.assertThat(value).isEqualTo(1);
}
@Test
void test2() {
value++;
System.out.println(value);
System.out.println(user);
Assertions.assertThat(value).isEqualTo(1);
}
}
1
com.example.securitysample.sample.User@14be750c
1
com.example.securitysample.sample.User@4fe2dd02
value ๊ฐ ๋ ํ ์คํธ ์ค ํ๋์์๋ 2๊ฐ ์ฐํ์ผ ํ ํ ๋ฐ ๋ ๋ค 1์ด ์ฐํ๋ค. ๋ฟ๋ง ์๋๋ผ user ๋ผ๋ ๊ฐ์ฒด์ ์ฃผ์๊ฐ๋ ๋ ํ ์คํธ์์ ๋ค๋ฅธ ๊ฒ์ ๋ณผ ์ ์๋ค. ๋ ํ ์คํธ์์ SampleTest ๋ผ๋ ํ ์คํธ ์ธ์คํด์ค๊ฐ ๋ฐ๋ก ๋ฐ๋ก ์์ฑ๋์ด ์ฌ์ฉ๋์๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
์ด๋ @Test๊ฐ ์๋ก์๊ฒ ์ฃผ๋ ์ํฅ์ ์์ ๊ธฐ ์ํด์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉ์๋ ๋ณ๋ก ์ธ์คํด์ค๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด ์ฃผ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌํ ๊ธฐ๋ณธ ์ฒ ํ์ด ์๊ธฐ ๋๋ฌธ์ ํ ์คํธ์ธ์คํด์ค์ ๋ผ์ดํ์ฌ์ดํด ๊ธฐ๋ณธ ์ ๋ต์ด PER_METHOD ์ด๋ค.
PER_CLASS ๋ก ๋ฐ๊พธ๊ฒ ๋๋ฉด @BeforeAll๊ณผ @AfterAll ์ static ์ฒ๋ฆฌ ํด์ค ํ์๊ฐ ์์ด์ง๋ค. ์ ์ด์ static ์ด์ด์ผ ํ๋ ์ด์ ๊ฐ ์ธ์คํด์ค๋ฅผ ํ ์คํธ ๋ง๋ค ์๋ก ๋ง๋๋๊น ๋ชจ๋ ํ ์คํธ ์ ๋๋ ํ์ ํธ์ถ์ ํ๊ธฐ ์ํด์ static ์ฒ๋ฆฌ๋ฅผ ํด์ค ๊ฒ์ธ๋ฐ ํ๋์ ์ธ์คํด์ค์์ ๋ชจ๋ ํ ์คํธ๋ฅผ ์๋ ์ํฌ ๊ฑฐ๋ฉด static ์ผ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค.
JUnit ํ
์คํธ ์์
ํ ํด๋์ค ๋ด์ @Test ๋ค์ ํน์ ํ ์์์ ์ํด ์คํ๋์ง๋ง ์ด๋ป๊ฒ ๊ทธ ์์๋ฅผ ์ ํ๋์ง๋ ์๋์ ์ผ๋ก ๋ถ๋ช ํ ํ์ง ์๋๋ค. ์ด์ ๋ ํ ์คํธ ์ธ์คํด์ค๋ฅผ @Test ๋ง๋ค ๋ ๋ฆฝ์ ์ผ๋ก ์์ฑํ๋ ๊ฒ๊ณผ ๊ฐ๋ค.
์ฆ, ์ ๋๋ก๋ Unit ํ ์คํธ๋ผ๋ฉด ๊ฐ Unit test๊ฐ์ ๋๊ฐ ๋จผ์ ์คํ๋๋ ์๋ก์๊ฒ ์ํฅ์ด ์์ด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.(๋ฉฑ๋ฑ์ฑ์ ๊ฐ๋ ๊ณผ ์ด๋์ ๋ ๊ต์งํฉ์ด ์๋ค๊ณ ์๊ฐ๋๋ค)
ํ์ง๋ง, ๊ฒฝ์ฐ์ ๋ฐ๋ผ์ ํน์ ํ ์์๋๋ก ํ ์คํธ๋ฅผ ์คํํ๊ณ ์ถ์ ๋๊ฐ ์๋๋ฐ, ์ด ๋์๋ @TestInstance(TestInstance.Lifecycle.PER_CLASS)๊ณผ ํจ๊ป @TestMethodOrder๋ฅผ ํตํด์ ๋ฉ์๋๋ง๋ค ์์๋ฅผ ์ ํ ์ ์๋ค.(๊ตณ์ด ๊ฐ์ด ์ธ ํ์๋ ์๋ค. ๋ค๋ง, ์์๋ฅผ ์ ํ๋ค๋ ๊ฒ์ด ํด๋์ค ๋ด ๊ณต์ ํ ์์์ด ์์ ํ๋ฅ ์ด ๋๊ธฐ์ ์์ ๋ฅผ ์ด๋ ๊ฒ ๋ค๋ฃฌ ๊ฒ)
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) // @Order ๋ผ๋ annotation์ผ๋ก order๋ฅผ ์ ํด์ฃผ๊ฒ ๋ค๊ณ ์ ์ธํ๋ ์๋ฏธ
class StudyTest {
// ๊ฐ ๋ฉ์๋์ @Order(int) ๋ก ์์๋ฅผ ์ง์ ํด์ค๋ค. int๊ฐ ์์์๋ก ๋จผ์ ์คํ.
}
junit-platform.properties
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.DisplayNameGenerator$ReplaceUnderscores
JUnit 5 ํ์ฅ ๋ชจ๋ธ
JUnit 4์ ํ์ฅ ๋ชจ๋ธ์ @RunWith(Runner), TestRule, MethodRule. JUnit 5์ ํ์ฅ ๋ชจ๋ธ ์ ๋จ ํ๋, Extension.
ํ์ฅํฉ ๋ฑ๋ก ๋ฐฉ๋ฒ
์ ์ธ์ ์ธ ๋ฑ๋ก @ExtendWith
ํ๋ก๊ทธ๋๋ฐ ๋ฑ๋ก @RegisterExtension
ํ์ฅํฉ ๋ง๋๋ ๋ฐฉ๋ฒ
ํ ์คํธ ์คํ ์กฐ๊ฑด
ํ ์คํธ ์ธ์คํด์ค ํฉํ ๋ฆฌ
ํ ์คํธ ์ธ์คํด์ค ํ-์ฒ๋ฆฌ๊ธฐ
ํ ์คํธ ๋งค๊ฐ๋ณ์ ๋ฆฌ์กธ๋ฒ
ํ ์คํธ ๋ผ์ดํ์ฌ์ดํด ์ฝ๋ฐฑ
์์ธ์ฒ๋ฆฌ
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@ExtendWith(ExecutionTimeExtension.class)
class StudyTest {
}
๋ง์ฝ ์์ ๊ฐ์ด THRESHOLD ์ ๊ฐ์ ํน์ ๊ฐ์ ์์ํ ํ์ง ์๊ณ ํ ์คํธ ๋ง๋ค ๋ค๋ฅด๊ฒ ์ค์ ํ๊ณ ์ถ๋ค๋ฉด @RegisterExtension์ ์ด์ฉํ๋ค.
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StudyTest {
@RegisterExtension
ExecutionTimeExtension executionTimeExtension = new ExecutionTimeExtension(1000L);
}
public class ExecutionTimeExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
private final long threshold;
public ExecutionTimeExtension(long threshold) {
this.threshold = threshold;
}
@Override
public void beforeTestExecution(ExtensionContext context) {
ExtensionContext.Store store = this.getStore(context);
store.put(START_TIME, System.currentTimeMillis());
}
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
ExtensionContext.Store store = this.getStore(context);
long duration = System.currentTimeMillis() - store.remove(START_TIME, long.class);
if (duration > threshold) {
System.out.println("over threshold");
}
}
private ExtensionContext.Store getStore(ExtensionContext context) {
String testClassName = context.getRequiredTestClass().getName();
String testMethodName = context.getRequiredTestMethod().getName();
return context.getStore(ExtensionContext.Namespace.create(testClassName, testMethodName));
}
}
Last updated