14. Assertions and Unit Testing
14.1 Assertions
assert()メソッドを使って書く。
assert(condition)
conditionが偽になるとAssertionError例外を投げる。
assert(condition, explanation)
AssertionErrorにexplanationからtoStringで取得した文字列を入れて投げる。
explanationはAny。scalaでは全ての型にtoStringが実装されている。
ensuring (w <= _.width)
assert()は何処に入れても良いが、良くあるのはreturnの直前。
この場合、assert()の後で保持したreturn値を返す様に書く必要がある。
ensuring()を使えばこれが簡単に出来る。
戻り値を引数に取ってBooleanを返すチェック関数を引数に取る。
関数の戻り値をチェックに渡してtrueなら戻り値を関数の戻り値として返す。
def function(): type = { ... } ensuring ((x: type): => {...}: Boolean)
?? ensuringに渡されているのは戻り値のimplicitly converted??
assersionやensuringはJVMの-ea,-daのcommand-line flagsで有効化できる。
有効にしたまま動かせば、実動作中に値の正常性をチェックできる。
ただし、この章ではテストデータを使った外部のユニットテストを扱う。
?? assert()を使うのは関数型の処理順非依存に反しない??
14.2 Unit testing in Scala
javaのunit test toolが使える。
JUnit, TestNG
scala用に作られたtoolもある
ScalaTest, specs, ScalaCheck
?? 2008年の段階なので今はもっと良いものがあるかも??
ここで扱うのはScalaTest
ScalaTestでも幾つかのテスト方法がある。
一番簡単なのはorg.scalatest.Suiteをmix-inしたクラスにtest methodsを定義する。
import org.scalatest.Suite
import Element.elem
class ElementSuite extends Suite {
def testUniformElement() {
val ele = elem('x', 2, 3)
assert(ele.width == 2)
}
}
インタープリタからも実行できる。
scala> (new ElementSuite).execute()
ScalaTestではsubtypeでexecuteをoverrideして他のスタイルを提供する。
関数値を使ったFunSuite
import org.scalatest.FunSuite
import Element.elem
class ElementSuite extends FunSuite {
test("elem result should have passed width") {
val ele = elem('x', 2, 3)
assert(ele.width == 2)
}
}
testと言うメソッドがprimary constructorから呼び出される。
{} はテスト用の関数値(by-name parameter)で登録されて後で実行される。
テスト関数の名前を付ける必要が無い。
テストの説明を記載出来る。
14.3 Informative failure reports
assert(ele.width == 2)
単に例外が投げられるだけでどんな値だったかは分からない。
assert(ele.width === 2)
"3 did not equal 2"等と出る。
expect(2) {
ele.width
}
"Expected 2, but got 3"等と出る。
intercept[IllegalArgumentException] {
elem('x', -2, 3)
}
IllegalArgumentExceptionが投げられないとNGになるテスト。
14.4 Using JUnit and TestNG
JUnit
Kent Beck and Erich Gammaが作ったもっともポピュラーなフレームワーク
JUnit3と4がある。
junit.framework.TestCaseをextendsすればscalaでも書ける。
書いたテストケースはJunit上で実行出来る。
org.scalatest.junit.JUnit3SuiteをextendsすればScalaTestのassertが使える。
書いたテストケースはJunit上でもScalaTest上でも実行出来る。
JUnit4はSection 29.2参照。
JUnitWrapperSuite
javaで書かれたJunitのテストをScalaTestで実行可能。
TestNG
Cedric Beust and Alexandru Popescu作のオープンソース
Junitと同様にscalaで書いたりjavaで書いたものをScalaTest上で実行出来る。
14.5 Tests as specifications
behavior-driven development (BDD)
コードの振る舞いを人が読める仕様記載する事が強調されている。
記載されている振る舞いを確かめるためのテストを付随させる。
ScalaTestにはSpecというtraitでこれをサポートしている。
import org.scalatest.Spec
class ElementSpec extends Spec {
describe("A UniformElement") {
it("should have a width equal to the passed value") {
val ele = elem('x', 2, 3)
assert(ele.width === 2)
}
it("should throw an IAE if passed a negative width") {
...
describeにsubject、テストの主題を記載して、
itにbehavior、個々の振る舞いを記載する。
テストの実行結果も可読性があるものになっている。
The specs testing framework
Eric Torreborre作のオープンソースで、違うBDDスタイルをサポートしている。
import org.specs._
object ElementSpecification extends Specification {
"A UniformElement" should {
"have a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width must be_==(2)
}
"throw an IAE if passed a negative width" in {
elem('x', -2, 3) must
throwA[IllegalArgumentException]
...
"must be_==", "must throwA"の様なmatcherを色々用意されている。
自作のmatcherを定義する事も出来る。
trait org.specs.SpecsMatchersをmix-inすればScalaTest,JUnit,TestNGでも使える。
specsは単独でも使えるし、ScalaTest, JUnitから実行する事も出来る。
14.6 Property-based testing
ScalaCheck
Rickard Nilsso作のオープンソース
特定のpropertyがある条件でテストを満たすかを確認できる。
import org.scalatest.prop.FunSuite
import org.scalacheck.Prop._
import Element.elem
class ElementSuite extends FunSuite {
test("elem result should have passed width", (w: Int) =>
w > 0 ==> (elem('x', w, 3).width == w)
)
...
implication operator, ==> 包含演算子
w > 0 ==> (elem('x', w, 3).width == w)
==>がポイント。左項が真なら右項も真になるはずと言う意味。
ScalaCheckが実際は数百のパターンを生成して試験を行ってくれる!!
?? パターン生成方法と数は??
ScalaTest's Checkers trait
import org.scalatest.junit.JUnit3Suite
import org.scalatest.prop.Checkers
import org.scalacheck.Prop._
import Element.elem
class ElementSuite extends JUnit3Suite with Checkers {
def testUniformElement() {
check((w: Int) => w > 0 ==> (elem('x', w, 3).width == w))
check((h: Int) => h > 0 ==> (elem('x', 2, h).height == h))
...
checkメソッドを使えば複数propertyを使ったテストが出来る?
?? 上の例では複数propertyのは使っていない??
14.7 Organizing and running tests
この章で紹介したフレームワークはテストを組織化する方法を提供している。
ScalaTestについてちょっと触れてみる。詳しくはScalaTestのドキュメント参照。
ScalaTestではSuiteを多段にネストして大規模なテストを構成する。
Suiteはツリー構造になってrootを指定すると配下全てが実施される。
ネスト登録は手動でも自動でも出来る。
ネストされるクラスのメソッドを上書きする。
登録用のSuperSuiteクラスのconstructorに渡す。
package nameをScalaTest's Runnerに登録する。
自動的にSuiteを探してroot Suite配下にネストする。
ScalaTest's Runnerをコマンドラインから実行してテストを実施できる。
antのtaskとしても実施出来る。
antと言うのはjavaのアプリケーションをビルドするためのツール。
Suiteの名前を指定する。
prefixを指定するとRunnerが自動的にSuiteを探して実行する。
オプションでテストのjarファイルのrunpathを指定できる。
結果の表示をコントロールするreporterを一つもしくは複数指定できる。
ScalaTestにはScalaTest自体の実装をテストするSuiteSuiteがある。
$ scala -cp scalatest-0.9.4.jar org.scalatest.tools.Runner
-p "scalatest-0.9.4-tests.jar" -s org.scalatest.SuiteSuite
-cpはScalaTestのjarのクラスパス
org.scalatest.tools.RunnerはRunner appのfully qualified name
-pはテストSuiteのjarのrunpath
-sは実行するてすとSuite
14.8 Conclusion
2パターンのテスト方法
実働コードにassertionを直接混ぜるケース
別途テストSuiteを作成するケース
javaのJUnitやTestNGも使えるし、ScalaTest,ScalaCheck,specsも使える。
scalaの言語仕様から少し離れても説明べき重要な技術と感じている。
0 件のコメント:
コメントを投稿