@ParameterlizedTest
는 인자를 받아서 여러 번 테스트를 실행한다.
테스트를 진행하다 보면 거의 같고 약간만 다른 테스트 케이스를 만날 수 있다.
이를 테면 어떤 주차장에 방문 예약하는 테스트를 만든다고 가정하면 아래와 같은 예외 케이스들이 나온다.
- 핸드폰 번호가 없으면
BadRequestException
이 발생한다. - 날짜가 잘못되면
BadRequestException
이 발생한다. - 차량번호가 잘못되면
BadRequestException
- 비활성화된 주차장이라면
ForbbidenException
이런 예외들이 발생할 수 있다.
이걸 하나씩 테스트하면 테스트 코드가 거의 중복된다.
@Test
fun `방문차량 예약 시 주차장이 비활성화 상태면 ForbbidenException이 발생한다`() {
// given
val params = listOf(getParam())
doReturn(주차장().apply{ 활성상태 = false }).`when`(parkingRepository).findById(any())
// when
assertThrows<ForbbidenException> { parkingService.reserve(params) }
// then
verify(parkingRepository, times(1)).findById(any())
}
@Test
fun `방문차량 예약 시 차량번호가 잘못된 형식이면 BadRequestException 발생한다`() {
// given
val params = listOf(getParam().apply { 차량번호 : "92록11" } )
doReturn(주차장().apply{ 활성상태 = true }).`when`(주차장Repository).findById(any())
// when
assertThrows<BadRequestException> { parkingService.reserve(params) }
// then
verify(parkingRepository, times(1)).findById(any())
}
//... 다른 테스트도 시나리오가 같음
이럴 때 틀은 두고 달라지는 부분만 파라미터화해서 하나의 메소드를 반복 사용한다. 이것이 Parameterlized Test다.
parameterlized 테스트에는 여러 인수를 받을 수 있다. csv, Enum, primitive type, null... 그 중에는 좀더 다양한 인수를 받을 수 있는 @MethodSource
라는 게 있다.
@MethodSource
이 어노테이션은 외브 클래스 또는 내부 클래스에 있는 하나 이상의 팩토리 메소드를 참조할 수 있다.
- 반드시
static
이어야 하고(Kotlin인 경우 companion object 내에 선언,@JvmStatic
붙여야 함. - 파라미터를 가질 수 없다.
- 속성 값으로 팩토리 메소드 이름을 명시해야 한다. (
@MethodSource(stringProvider)
)
팩토리 메소드는 paramterlized 테스트에 넘길 인수 스트림을 생성해야 한다.
IntStream
, Stream
등 모두 사용할 수 있고, 여러 매개 변수를 넘겨야 할 때는 Arguments.of()
를 사용할 수도 있다.
위에서 중복된 테스트를 파라미터화된 테스트로 변경하면 이렇게 할 수 있다.
@ParameterizedTest(name = "{index} : 방문차량 예약 시 {3}")
@MethodSource("reservationParameters")
fun `방문차량 예약 시 입력 값이 잘못되면 예외 발생한다`(zone: 주차장, param: Param, throwable: Class<Throwable>, displayName: String) {
// given
val params = listOf(param)
doReturn(Optional.of(zone)).`when`(parkingRepository).findById(any())
// when
assertThrows(throwable) { parkingService.reserve(params) }
// then
verify(parkingRepository, times(1)).findById(any())
}
companion object {
var parkingWhitelistCreateParam = Param(/*생성*/)
@JvmStatic
fun reservationParameters(): List<Arguments> {
return listOf(
Arguments.of(
주차장().apply { 활성상태 = false },
param, ForbiddenException::class.java,
"주차장 활성화 안 되어 있으면 ForbiddenException"),
Arguments.of(
주차장(),
param.apply { 차량번호 = "1콩292" },
BadRequestException::class.java,
"유효하지 않은 차량번호 입력하는 경우 BadRequestException"
),
Arguments.of(
주차장(),
param.apply {
예약시간 = LocalDateTime.now().plusHours(2)
예약종료시간 = LocalDateTime.now().minusHours(11)
},
BadRequestException::class.java,
"잘못된 시간에 신청하는 경우 BadRequestException"
),
//....
)
}
}
@ParameterizedTest(name = "{index} : 방문차량 예약 시 {3}")
이걸 보면 알 수 있지만 {index} 형태로 몇 번째 테스트인지에 대한 index를 사용할 수 있다. {3}은 네 번째 매개변수를 의미한다. 이것을 displayName처럼 사용할 수 있다.
결론
테스트는 어렵다. holy shit
'Study' 카테고리의 다른 글
type check는 왜 필요한가? (0) | 2022.05.18 |
---|---|
테스트 대역 (0) | 2021.12.21 |
파일업로드 input[files] FileList 동적으로 변경하기 (11) | 2021.06.12 |
[Database] Transaction, Lock (0) | 2021.05.25 |
Flyway (0) | 2021.05.16 |