JUnit์ด๋?
๐ฒ Java ๊ฐ๋ฐ์์ 93%๊ฐ ์ฌ์ฉํ๋ ๋จ์ ํ ์คํธ ํ๋ ์์ํฌ
๐ฒ ์คํ๋ง ๋ถํธ 2.2๋ฒ์ ์ด์๋ถํฐ ๊ธฐ๋ณธ ์ ๊ณตํ๋ค.
JUnit5๋?
๐ฒ Platform : ํ ์คํธ๋ฅผ ์คํํ๊ฒ ํด์ฃผ๋ ๋ฐ์ฒ๋ฅผ ์ ๊ณตํ๋ค. TestEngine API๋ฅผ ์ ๊ณต.
๐ฒ Jupiter : JUnit5๋ฅผ ์ง์ํ๋ TestEngine API ๊ตฌํ์ฒด
๐ฒ Vintage : JUnit4์ 3๋ฅผ ์ง์ํ๋ TestEngine ๊ตฌํ์ฒด
JUnit5๋ฅผ ์์ํ๋ ๋ฐฉ๋ฒ
๐ฒ ์คํ๋ง๋ถํธ 2.2๋ฒ์ ์ด์๋ถํฐ๋ ๊ธฐ๋ณธ ํ์ฌ๋๋ค.
๐ฒ ๊ทธ ์ธ์๋ maven, ํน์ gradle ๋ฐฉ์์ผ๋ก ์ถ๊ฐํ ์ ์๋ค.
gradle
test {
useJUnitPlatform()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
maven
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
Annotations
๐ถ @Test
ํ ์คํธ ๋ฉ์๋์์ ๋ํ๋ด๋ ์ด๋ ธํ ์ด์ ์ด๋ค. JUnit4์๋ ๋ค๋ฅด๊ฒ ์ด๋ ํ ์์ฑ๋ ๊ธฐ์ฌํ์ง ์๋๋ค.
@Test
void create() {
...
}
๐ถ ์๋ช ์ฃผ๊ธฐ(LifeCycle) ์ด๋ ธํ ์ด์
๐ฒ @BeforeAll : ํด๋น ํด๋์ค์ ์์นํ ๋ชจ๋ ํ ์คํธ ๋ฉ์๋ ์คํ ์ , ํ ๋ฒ๋ง ์คํ๋๋ ๋ฉ์๋
๐ฒ @AfterAll : ํด๋น ํด๋์ค์ ์์นํ ๋ชจ๋ ํ ์คํธ ๋ฉ์๋ ์คํ ํ, ํ ๋ฒ๋ง ์คํ๋๋ค.
๐ฒ @BeforeEach : ํด๋น ํด๋์ค์ ์์นํ ๋ชจ๋ ํ ์คํธ ๋ฉ์๋ ์คํ ์ ์ ์คํ๋๋ ๋ฉ์๋, BeforeAll๊ณผ ๋ฌ๋ฆฌ ๋ชจ๋ ๋ฉ์๋ ๋ง๋ค ์คํ๋๋ค.
๐ฒ @AfterEach : ํด๋น ํด๋์ค์ ์์นํ ๋ชจ๋ ํ ์คํธ ๋ฉ์๋ ์คํ ํ์ ์คํ๋๋ ๋ฉ์๋, AfterAll๊ณผ ๋ฌ๋ฆฌ ๋ชจ๋ ๋ฉ์๋๋ง๋ค ์คํ๋๋ค.
๐ถ @DisplayName
์ด๋ค ํ ์คํธ์ธ์ง ์ฝ๊ฒ ํํํ ์ ์๋๋ก ํด์ฃผ๋ ์ด๋ ธํ ์ด์ ์ด๋ค. ๊ณต๋ฐฑ์ด๋, Emoji, ํน์๋ฌธ์ฅ ๋ฑ ๋ชจ๋ ์ง์ํ๋ค.
@DisplayName("ํ
์คํธ์
๋๋ค")
void test() {
...
}
๐ถ RepeatedTest
ํน์ ํ ์คํธ๋ฅผ ๋ฐ๋ณต์ํค๊ณ ์ ํ ๋ ์ฌ์ฉํ๋ค. ๋ฐ๋ณต ํ์๋ _value_ ์์ฑ์ ํตํด ์ค์ ํ๊ณ , _name_์์ฑ์ ํตํด ๊ทธ ์ด๋ฆ๋ ์ง์ด์ค ์ ์๋ค.
@RepeatedTest(value = 10, name = "")
@DisplayName("๋ฐ๋ณต")
void repeatedTest() {
...
}
๐ถ @ParameterizedTest
ํ ์คํธ์ ์ฌ๋ฌ ๋ค๋ฅธ ๋งค๊ฐ๋ณ์๋ฅผ ๋์ ํด๊ฐ๋ฉฐ ๋ฐ๋ณต ์คํํ ๋ ์ฌ์ฉํ๋ค. ์์ฃผ ์ฌ์ฉ๋๋ ์ด๋ ธํ ์ด์ ์ด๋ผ ๊ธฐ์ตํด๋ ๋งํ๋ค.
๐ฒ CsvSource
@ParameterizedTest
@CsvSource(value = {"ACE,ACE:12", "ACE,ACE,ACE:13"}, delimiter = ":")
@DisplayName("")
void useParameterizedTest(final String input, final int expected) {
}
key: value๋๋์ผ๋ก input๊ณผ expect์ :๋ฅผ ๊ธฐ์ค์ผ๋ก split๋ ๊ฐ๋ค์ด ๋ค์ด์จ๋ค.
๐ฒ ValueSource
@ParameterizedTest
@ValueSource(strings = {"", " "})
void isBlankTest(String input) {
assertTrue(Strings.isBlank(input));
}
test ๋ฉ์๋ ์คํ ์์ input ๊ฐ์ผ๋ก ํ๋์ ์ธ์๊ฐ ์ ๋ฌ๋๋ค. ์ด๋, ๋ฆฌํฐ๋ด ๊ฐ์ ๋ฐฐ์ด์ ํ ์คํธ ๋ฉ์๋๋ก ์ ๋ฌํ๋ค.
๐ฒ Null and Empty Values
_@NullSource_๋ฅผ ์ฌ์ฉํ๋ฉด ์ธ์๋ก null๊ฐ์ ์ ๋ฌํ๊ณ , _@EmptySource_๋ฅผ ์ฌ์ฉํ๋ฉด ์ธ์๋ก ๋น ๊ฐ์ ์ ๋ฌํ๋ค. ๋ง์ฝ _@NullAndEmptySource_๋ฅผ ์ฌ์ฉํ๋ฉด null๊ฐ๊ณผ ๋น ๊ฐ ๋ชจ๋๋ฅผ ์ ๋ฌํ ์ ์๋ค.
ํน์ดํ ๊ฒ์ _@EmptySource_์ _@NullAndEmptySource_๋ ๋ฌธ์์ด, ์ปฌ๋ ์ , ๋ฐฐ์ด์์๋ ๋์ํ๋ค.
@ParameterizedTest
@NullAndEmptySource
void isBlankTest(String input) {
asertTrue(Strings.isBlank(input));
}
๐ฒ Enum
_@EnumSource_๋ฅผ ํตํด ์ด๊ฑฐํ ๊ฐ์ ๋ฐฐ์ด์ ํ ์คํธ ๋ฉ์๋๋ก ์ ๋ฌํ ์ ์๋ค. ์ด๋, ํ๋์ ์ธ์๋ง์ ์ ๋ฌํ๋ค.
์์ฑ์ผ๋ก๋ _name_์ด ์๋๋ฐ, ์ ๊ท ํํ์์ ์ ๋ฌํ ์ ์์ผ๋ฉฐ, _mode_๋ผ๋ ์์ฑ์ผ๋ก ์ ์ธํ enum์ ์ง์ ํ ์๋ ์๋ค.(EXCLUDE)
@ParameterizedTest
@EnumSource(
value = Month.class,
names = {"APRIL", "JUNE", "SEPTEMBER", "NOVEMBER", "FEBRUARY"},
mode = EnumSource.Mode.EXCLUDE)
void exceptFourMonthsTest(Month month) {
final boolean isALeapYear = false;
assertEquals(31, month.length(isALeapYear));
}
๐ถ @Nested
ํ ์คํธ ํด๋์ค ์์์ ๋ด๋ถ ํด๋์ค๋ฅผ ์ ์ํ์ฌ ํ ์คํธ๋ฅผ ๊ณ์ธตํํ ๋ ์ฌ์ฉํ๋ค.
Assertions
ํ ์คํธ ์ผ์ด์ค์ ์ํ ๊ฒฐ๊ณผ๋ฅผ ํ๋ณํ๋ ๋ฉ์๋์ด๋ค. ๋ํ ๋ชจ๋ JUnit Jupiter Assertions๋ static ๋ฉ์๋์ด๋ค.
import static org.assertj.core.api.Assertions.*;
Object Assertion
public class Dog {
private String name;
private Float weight;
}
Dog fido = new Dog("Fido", 5.25);
Dog fidosClone = new Dog("Fido", 5.25);
์ฃผ์ด์ง ์ฝ๋์ ๋ํด์ ์๋์ ๊ฐ์ด ํ ์คํธํ ์ ์๋ค.
assertThat(fido).isEqualTo(fidosClone);
// ๋ด์ฉ ์์ฒด๋ฅผ ๋น๊ตํ๊ธฐ ์ํด์๋ ์๋ ์ฌ์ฉ
assertThat(fido).isEqualToComparingFieldByFieldRecursively(fidosClone);
Boolean Assertion
assertThat("".isEmpty()).isTrue();
Iterable/Array Assertion
๐ถ Iterable/Array๊ฐ ํน์ ์์๋ฅผ ์ง๋๋ ์ง ํ ์คํธ
List<String> list = Arrays.asList("1", "2", "3");
assertThat(list).contains("1");
๐ถ๋น ๊ฐ์ธ ์ง ํ์ธ
assertThat(list).isNotEmpty();
๐ถ ์ฒซ ๋ฒ์งธ ์์๊ฐ ํ์ธ
assertThat(list).startsWith("1");
๐ถ ์ฒด์ด๋์ ํตํ ์ฌ์ด ํ ์คํธ
assertThat(list)
.isNotEmpty()
.contains("1")
.doesNotContainNull()
.containsSequence("2", "3");
Character Assertion
assertThat(someCharacter)
.isNotEqualTo('a')
.inUnicode()
.isGreaterThanOrEqualTo('b')
.isLowerCase();
Map Assertion
key/value๋ entry๋ฅผ ์ง๋ Map ์๋ฃ๊ตฌ์กฐ์ ๋ํด์๋ ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
assertThat(map)
.isNotEmpty()
.containsKey(2)
.doesNotContainKeys(10)
.contains(entry(2, "a"));
๐ถ assertAll(executables...)
๋งค๊ฐ ๋ณ์๋ก ๋ฐ๋ ๋ชจ๋ ํ ์คํธ์ฝ๋๋ฅผ ํ ๋ฒ์ ์คํํ๋ค. ๊ทธ๋์ ์ค๋ฅ๊ฐ ๋๋ ๋๊น์ง ์คํํ ํ์ ํ ๋ฒ์ ๋ชจ์์ ์ถ๋ ฅํ๋ค.
assertAll(
() -> assertNotNull(study),
() -> assertEquals(Status.STARTED, study.getStatus(), "์ฒ์ ์ํ๊ฐ DRAFT"),
() -> assertTrue(study.getLimit() > 0, "์ต๋ ์ธ์์ 0๋ณด๋ค ์ปค์ผํ๋ค.")
);
์ด์ธ์ Assertion
๐ถ assertThrows(extectedType, excutable)
์์ธ ๋ฐ์์ ํ์ธํ๋ ํ ์คํธ์ด๋ค. ๋ก์ง์ด ์คํ๋๋ ๋์ค ์์ธ๋ฅผ ๋ฐํ๋ฐ์์, ์์ธ์ ์ํ๊น์ง ์ ์ ์๋ค.
@Test
void exceptionThrow() {
Exception e = assertThrows(Exception.class, () -> new Test(-10));
assertDoesNotThrow(() -> System.out.println("Do Something"));
}
์ฒด์ด๋์ ํตํด์ ์๋์ ๊ฐ์ด ํด๋์ค์ ์ข ๋ฅ๋ ๋ฉ์ธ์ง๋ ์์๋ผ ์ ์๋ค.
// when, then
assertThatThrownBy(() -> TryCountValidator.validate(input))
.isInstanceOf(NullPointerException.class)
.hasMessage("์
๋ ฅ๊ฐ์ด ๊ณต๋ฐฑ์ด๋ฉด ์๋ฉ๋๋ค.");
๐ถ assertTimeout(duration, excutable)
ํน์ ์๊ฐ ์์ ์คํ์ด ์๋ฃ๋๋์ง ํ์ธํ๋ค. _Duration_์ ์ํ๋ ์๊ฐ์ด๋ฉฐ, _Executable_์ ํ ์คํธํ ๋ก์ง์ด๋ค.
@Test
@DisplayName("ํ์์์ ์ค์")
void timeoutNotExceeded() {
assertTimeout(ofMinutes(2), () -> Thread.sleep(10));
}
๐ถ Assumption
์ ์ ๋ฌธ์ด true๋ผ๋ฉด ์คํํ๊ณ , false๋ผ๋ฉด ์ข ๋ฃํ๋ค. CI์ ๊ฐ์ด ํน์ ํ๊ฒฝ์์๋ง ํ ์คํธ๋ฅผ ์ํํ์ฌ์ผ ํ ๋ ์ฌ์ฉํ ์ ์๋ค.
๐ฒ assumeTrue : false์ผ ๋, ํ ์คํธ ์ ์ฒด๊ฐ ์คํ๋์ง ์๋๋ค.
๐ฒ assumingThat : ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋ ์ฝ๋๋ธ๋ญ๋ง ์คํ๋์ง ์๋๋ค.
void some_test() {
assumeTrue("DEV".equals(System.getenv("ENV")), () -> "๊ฐ๋ฐ ํ๊ฒฝ์ด ์๋๋๋ค.");
assertEquals("A", "A")' // ์ถ๋ ฅ x
}
References
https://gmlwjd9405.github.io/2019/11/27/junit5-guide-parameterized-test.html
'๐ซ Language > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Java] ๋ชจ๋ ๊ฐ์ฒด๋ ๋ถ๋ณ ๊ฐ์ฒด์ฌ์ผ ํ ๊น? (0) | 2024.02.28 |
---|---|
[Java] ๊ฐ์ฒด ์งํฅ ์ค๊ณ 5์์น(SOLID)์ด๋? (2) | 2024.01.13 |
[Java] ์ผ๊ธ ์ปฌ๋ ์ ๊ณผ ์ฌ์ฉํด์ผ ํ๋ ์ด์ ์ ๋ํ์ฌ (2) | 2024.01.13 |
[Java] ๋ฉํฐ์ค๋ ๋์ ๋๊ธฐํ์ ๋ํด์ (1) | 2024.01.06 |
[Java] ๋๊ธฐ/๋น๋๊ธฐ & ๋ธ๋ญํน/๋ ผ๋ธ๋ญํน์ด๋? (0) | 2024.01.02 |