이 글은 Programming in Scala 4/e
Chapter14를 읽고 작성한 글입니다.
단언문과 테스트는 작성한 소프트웨어가 제대로 동작하는 지 확인하는 방법이다.
스칼라는 assert 메서드를 호출하는 방식으로 단언문을 작성한다. 단언문은 조건을 만족하지 않는 경우 AssertionError
를 발생시킨다.
assert(조건, 설명)
위의 메서드는 조건을 만족하지 않는 경우 설명을 포함하는 AssertionError를 발생시킨다.
assert
메서드는 AssertionError
객체 안에 있는 toString
메서드를 호출한다.
def above(that:Element):Element = {
val this1 = this widen that.width
val that1 = that widen this.width
assert(this1.width == that1.width)
elem(this.contents ++ that1.contents)
}
위는 2개의 값이 같은지를 확인 후, 다르다면 에러를 띄우는 코드이다. 만약 위의 코드를 간결하게 하고 싶다면 Predef에 있는 ensuring
이라는 도우미 메서드를 사용해 작성할 수 있다.
private def widen(w: Int):Element =
if(w <= width) this
else {
val left = elem(' ', (w - width) / 2, height)
var right = elem(' ', w - width - left.width, height)
left beside this beside right
} ensuring (w <= _.width)
ensuring
메서드는 암시적 변환을 사용하므로 어떠한 결과 타입이든 적용할 수 있다.
JVM에 -ea
나 -da
명령행 옵션을 사용하면 확인의 동작을 켜거나 끌 수 있다.
작동 시, 각 단언문은 소프트웨어가 실행되면서 접하는 실제 데이터를 사용하는 작은 테스트 역할을 한다.
그렇다면 외부 단위 테스트는 어떻게 작성할까? 단위 테스트는 스스로 데이터를 제공하며, 애플리케이션과 독립적으로 실행할 수 있다.
테스트 도구는 매우 다양하다. Java 기반이므로 유명한 자바 테스트 도구인 J Unit을 사용할 수 있고, TestNg를 사용할 수 있다. 그리고, 스칼라로 만들어진 ScalaTest나 specs2, ScalaCheck 등이 있다.
이 중에서 ScalaTest
는 가장 유연한 스칼라 테스트 프레임워크로, 다른 문제를 풀기 위해 쉽게 커스터마이즈할 수 있다.
import org.scalatest.funsuite.AnyFunSuite
import Element.elem
class ElementSuite extends AnyFunSuite {
test("elem result should have passed with"){
val elem = elem('x', 2, e)
assert(ele.width == 2)
}
}
스칼라 테스트에서 중심적인 개념은 suite
이다. 테스트는 시작해서 성공하거나, 실패하거나 결과를 기다림, 혹은 취소될 수 있다.
트레이트 suite는 테스트들을 실행하기 위해 사전에 준비된 '생명 주기 메서드'들을 선언한다. 스칼라 테스트는 다른 테스트 스타일을 지원하기 위해 Suite를 확장하고 생명주기 메서드를 오버라이드하는 style trait를 지원한다.
위쪽에서 assert문을 실패하면 AssertionError
를 발생시키는 것을 알게되었다.
위의 코드처럼 조건이 틀리면 에러를 발생시킨다. 하지만, 에러 메시지만을 보고서는 어디서 어떤 오류가 발생했는지 알기 힘들다.
즉, 단언문이 실패한다면 파일명
, 실패한 단언문의 줄 번호
, 정보가 담긴 오류 메시지
를 출력해야 한다.
이를 위해서는 assertResult
메서드를 사용하면 된다.
위와 같이 assertResult
를 사용하면 빨간 글씨로 어디서 어떤 값을 도출해 실패했는지 알 수 있다.
(이 내용 실습을 하며 오류를 많이 겪었는데 이를 해결하는 방법은 여기에 기술하였다.)
동작 주도 개발(BDD/Behavior Driven Development) 테스트 스타일은 기대하는 코드의 동작을 사람이 읽을 수 있는 명세로 작성 후, 코드가 그 명세에 따라 잘 작동하는 지 확인하는 테스트를 작성하는데 중심을 둔다.
import org.scalatest.flatspec.AnyFlatSpect
import org.scalatest.matchers.should.Matchers
import Element.elem
class ElementSpect extends AnyFlatSpect with Matchers {
"A UniformElement" should "have a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width should be (2)
}
}
AnyFlatSpec
에서는 명세절(specifier clause)를 사용해 테스트를 작성한다. 테스트할 주제에 대해 이름을 문자열로 붙인다. 그 후, should
또는 must
나 can
을 넣고 그 뒤에 해당 주제의 작동을 설명하는 문자열이 오고 그 다음 in이 온다. in
이후의 중괄호에 테스트할 코드를 작성한다.