테스트를 작성할 때 실패 테스트를 작성하는 것이 성공테스트를 작성하는 것보다 더욱 중요합니다.
실패 경로는 대개 throw exception으로 처리되므로 ScalaTest는 이를 위한 테스트 방법을 제공해줍니다.
assertThrows
가장 기본적인 exception 테스트 방법입니다.
assertThrows 함수 내부에서 exception이 throw 되면 exception의 타입을 검사합니다.
assertThrows[ArithmeticException] {
5 / 0
}
thrownBy
should나 must matchers에서 제공해주는 방법으로 exception을 저장하고 추가적인 검증도 진행이 가능해 assertThrows 보다 더욱 활용성이 높은 테스트 방법입니다.
// an [T] 를 통해 어떠한 Exception type이 반환될지 검사합니다.
// thrownBy 뒤에는 exception이 던져질 코드를 작성하면 됩니다.
an [IndexOutOfBoundsException] should be thrownBy "str".charAt(-1)
// the [T]를 통해 Exception을 저장할 수 있습니다.
val exception = the [IndexOutOfBoundsException] thrownBy "str".charAt(-1)
exception.getMessage shouldBe "String index out of range: -1"
// 한 줄로 검증할 수도 있습니다.
the [ArithmeticException] thrownBy 1 / 0 should have message "/ by zero"
Try with Either 테스트 (추천)
테스트 코드를 작성할 때 주로 준비과정, 실행, 결과 검증의 세 부분으로 나눠서 작성하고 이를 Arrange Act Assert, Given When Then이라고 나눠서 표현하기도 합니다.
이렇게 부분을 나눠서 작성하면 코드의 작성자도 작성하기 편하고, 코드를 읽는 사람도 읽기 편해서 이런 식의 코드 작성을 추천하는데, 위의 assertThrows, thrownBy는 When과 Then의 순서가 뒤집어져서 기존 코드와 순서가 맞지 않아 이해하는데 애로사항이 생깁니다.
이런 상황에서 추천하는 방법으로 기존의 코드에서 검증받으려는 함수를 Try로 감싸고 결과를 Either로 변환하기를 추천합니다.
Try로 감싸고 Either로 변환하면 기존의 순서대로 Given When Then의 테스트가 가능해서 순서를 해치지도 않고, 크게 어려운 작업도 아니어서 테스트 작성에 문제가 생기지도 않습니다.
또, 예상외의 상황에서 throw가 발생해 테스트 실패의 StackDepth가 표시되지 않는 문제도 해결이 가능해집니다.
// given
val number = 5
// when
val actual = Try(number / 0).toEither
// then
actual.left.value shouldBe a[ArithmeticException]
더욱 자세한 Either 테스트 방법은 이전 글을 참고해주시면 되겠습니다.