* JUnit5 사용
1. Dynamic Test란?
* @Test 주석이 달린 표준 테스트는 컴파일 시간에 정의되는 static 한 테스트이다.
* Dynamic Test는 런타임 중에 생성되는 테스트
* @TestFactory 주석이 달린 팩토리 메소드에 의해 생성
* 팩토리 메소드는 DynamicTest 인스턴스의 Stream, Collection, Iterator를 반환해야 함
* @BeforeEach / @AfterEach 와 같은 생명주기 관련 어노테이션을 지원하지 않음
2. Dynamic Test 예제
* @TestFactory method는 dynamic test라는 것을 명시해준다.
* dynamic test는 컬렉션을 반환함
* 이름, 실행 두 부분으로 나뉨
@TestFactory
fun dynamicTestWithCollection(): List<DynamicTest> {
return listOf(
DynamicTest.dynamicTest("Add test") {
assertEquals(2, Math.addExact(1, 1))
},
DynamicTest.dynamicTest("Multiply Test") {
assertEquals(4, Math.addExact(2, 2))
}
)
}
* 전달된 이름을 출력한다.
* Iterable , Iterator, Stream 반환 하도록 작성 가능
@TestFactory
fun dynamicTestsWithIterable(): Iterable<DynamicTest> {
return listOf(
DynamicTest.dynamicTest("Add test") {
assertEquals(2, Math.addExact(1, 1))
},
DynamicTest.dynamicTest("Multiply Test") {
assertEquals(4, Math.addExact(2, 2))
}
)
}
@TestFactory
fun dynamicTestsWithIterator(): Iterator<DynamicTest> {
return listOf(
DynamicTest.dynamicTest("Add test") {
assertEquals(2, Math.addExact(1, 1))
},
DynamicTest.dynamicTest("Multiply Test") {
assertEquals(4, Math.addExact(2, 2))
}
).iterator()
}
@TestFactory
fun dynamicTestsFromIntStream(): Stream<DynamicTest> {
return IntStream.iterate(0) { n -> n + 2 }
.limit(10)
.mapToObj { n ->
DynamicTest.dynamicTest("test $n") { assertTrue(n % 2 == 0) }
}
}
3. DynamicTests Stream
* 도메인 이름을 입력하면 IP를 반환하는 예제를 작성해보자
* DomainNameResolver라는 가상의 라이브러리를 작성해 IP를 반환 받음
class DomainNameResolver {
private val ipByDomainName: MutableMap<String, String> = HashMap()
init {
ipByDomainName["https://www.naver.com"] = "23.220.128.185"
ipByDomainName["https://www.google.com"] = "142.250.217.100"
ipByDomainName["https://www.daum.net"] = "211.249.220.24"
}
fun resolveDomain(domainName: String): String? {
return ipByDomainName[domainName]
}
}
* @TestFactory method에서 Stream을 반환하도록 작성
public class DynamicTest extends DynamicNode {
...
public static <T> Stream<DynamicTest> stream(Iterator<T> inputGenerator,
Function<? super T, String> displayNameGenerator, ThrowingConsumer<? super T> testExecutor) {
...
}
* inputGenerator는 도메인 주소를 하나씩 반환.
* displayNameGenerator는 테스트 케이스의 고유한 이름을 제공한다.
* testExecutor는 DomainNameResolver을 활용해 inputGenerator의 값에 대응하는 도메인 값들을 생성하고 outputList에 대한 assertion을 생성한다.
@TestFactory
fun dynamicTestsFromStream(): Stream<DynamicTest> {
val inputList: List<String> = listOf("https://www.naver.com", "https://www.google.com", "https://www.daum.net")
val outputList: List<String> = listOf("23.220.128.185", "142.250.217.100", "211.249.220.24")
val domainNameResolver = DomainNameResolver()
val inputGenerator: Iterator<String> = inputList.iterator()
val displayNameGenerator = Function<String, String> { name -> "Resolving: $name" }
val testExecutor = ThrowingConsumer { input: String ->
val index = inputList.indexOf(input)
assertEquals(outputList[index], domainNameResolver.resolveDomain(input))
}
return DynamicTest.stream(
inputGenerator, displayNameGenerator, testExecutor
)
}
* inputList로부터 생성한 stream을 map을 통해 DynamicTest Stream으로 전환하여 반환하는 방법도 있음
@TestFactory
fun dynamicTestsFromStreamSimple(): Stream<DynamicTest> {
val inputList: List<String> = listOf("https://www.naver.com", "https://www.google.com", "https://www.daum.net")
val outputList: List<String> = listOf("23.220.128.185", "142.250.217.100", "211.249.220.24")
val domainNameResolver = DomainNameResolver()
return inputList.stream()
.map { DynamicTest.dynamicTest("Resolving: $it") {
val id = inputList.indexOf(it)
assertEquals(outputList[id], domainNameResolver.resolveDomain(it))
}
}
}
* 조금더 응용한다면 input과 output을 정해두고 테스트케이스를 작성할 수 있다.
@TestFactory
fun dynamicTestsFromStreamCase(): Stream<DynamicTest> {
class Case(
val domain: String,
val ip: String,
val expected: Boolean
)
val domainNameResolver = DomainNameResolver()
return listOf(
Case("https://www.naver.com", "23.220.128.185", true),
Case("https://www.google.com", "142.250.217.100", true),
Case("https://www.daum.net", "wrong ip", false)
).stream().map {
DynamicTest.dynamicTest("domain : $it") {
assertEquals(it.expected, it.ip == domainNameResolver.resolveDomain(it.domain))
}
}
}
4. 마치며
이러한 동적 테스트 케이스는 여러가지 이점을 줄수있다. 개인적으로는 구현에 집중하지 않고 설계에 더 집중할 수 있다고 생각한다.
동적 테스트를 짜다보면 구현에 종속적 테스트 코드는 작성하기 어렵고 그렇게 되면 다시한번 자신의 코드를 볼수있지 않을가 생각한다.
'IT > TDD & Test' 카테고리의 다른 글
[JUnit] 파일 & 리소스 (/src/test/resources) 가져오기 (0) | 2022.08.01 |
---|---|
[JUnit] 객체 타입 체크 (0) | 2022.07.26 |
[JUnit] JUnit에서 현재 실행 중인 테스트 이름 가지고 오기 (0) | 2022.07.25 |
RestClientTest를 알아보자~ (0) | 2021.03.27 |
Controller Test를 해보자~! (0) | 2021.03.21 |