Locked History Actions

borachio/scaladoc

本家scaladocの翻訳

以下は2011/7/2時点のhttp://borachio.com/api/com/borachio/package.htmlの翻訳

Borachio: Native Scala mocking

ScalaTestでBorachioを使うには、suiteにMockFactoryトレイトをミックスする。

class MyTest extends Suite with MockFactory

JUnit3にて使うには同じくMockFactoryTestCaseにミックスする。

class MyTest extends TestCase with MockFactory

訳注はじめ:なぜかJUnit4について記述がないが、以下のようにすれば使える。

import com.borachio._
class SomeTest extends AbstractMockFactory {

  @Before def setup() { resetExpectations }
  @After def after() { verifyExpectations }

  @Test def someTest() {
     ....
  }

:訳注おわり

Borachioは二つのモックスタイルをサポートする。ファンクショナルモッキングとプロキシモッキングである。

ファンクショナルモッキング

ファンクショナルモックはmockFunctionを使って生成される。例えば以下では一つのInt引数をとり、Stringを返すモック関数を作成する。

val m = mockFunction[Int, String]

そしてこのモックファンクションにエクスペクテーションを設定する。 例えば以下では、このモックが一度だけ42という引数で呼び出され、"Forty two"を返すものとする。

m expects (42) returning "Forty two" once

プロキシモッキング

mock(関数)によってプロキシモックを作成する。例えば以下は、Turtleトレイト(インターフェース)のすべてを実装するモックを作成する。

val m = mock[Turtle]

そしてそれぞれのメソッドについてのエクスペクテーションをセットすることができる。例えば

m expects 'setPosition withArgs (10.0, 10.0)
m expects 'forward withArgs (5.0)
m expects 'getPosition returning (15.0, 10.0)

エクスペクテーション

フ関数やメソッドがどのような引数で何度呼ばれるべきであるかをエクスペクテーションとしてセットすることができる。 加えて、モックはそのエクスペクテーションが成立する場合にどのような値を返すか、あるいは例外を発生するかを指定することができる。

引数

ファンクショナルモックに期待される引数を指定するにはexpectsを用いる。 プロキシモックにはwithArgsあるいはwithArgumentsを用いる。

期待する引数の指定がなければ、モックはいかなる引数をも受け入れる。

単純な等価性テストのための引数を指定する場合は、それらをタプルとして指定する(訳注:?)

m expects ("this", "that")

Borachioは現在のところ、二つのタイプの一般化マッチングをサポートしている。 ワイルドカードとイプシロンマッチングである。

ワイルドカード

ワイルドカード値は、*(アスタリスク)で表す。例えば

m expects ("this", *)

は次にマッチする。

m("this", 42)
m("this", 1.0)
m("this", null)

イプシロンマッチング

イプシロンマッチングは浮動小数点数値を扱う場合に便利だ。 イプシロンマッチは~(チルダ)オペレータで表される。

m expects (~42.0)

は次にマッチする。

m(42.0)
m(42.0001)
m(41.9999)

が次にはマッチしない。

m(43.0)
m(42.1)

返り値

モックの返り値をreturnsあるいはreturningを使って指定することができる。

m1 returns 42
m2 expects ("this", "that") returning "the other"

返り値が指定されない場合、ファンクショナルモックはnull.asInstanceOf[R]を返すが、Rは返り値のタイプである(つまり、Intの場合は0、Doubleの場合は0.0など)。

プロキシモックの場合はnullを返す。 これは多くの返り値型について正しく動作するが、プリミティブタイプ(Int, Double等)の場合にはそうはいかない。nullを返すとNullPointerExceptionが発生してしまうのである。 だから、このようなメソッドについては明示的に返り値を指定した方がよい。この制限は将来緩和されるかもしれない。

例外

値を返す代わりに例外を投げさせることもできる。

m expects ("this", "that") throws new RuntimeException("what's that?")

呼び出し回数

デフォルトでは、モックは1回以上呼び出されることを期待する(つまり、全く呼び出されない場合は「失敗」になる)。 repeatを使って正確な呼び出し回数を指定することができる。

m1 returns 42 repeat 3 to 7
m2 expects (3) repeat 10

これについては、様々な別の書き方もできる。

m1 expects ("this", "that") once
m2 returns "foo" noMoreThanTwice
m3 expects (42) repeated 3 times

すべてのリストは「Expectation」を参照して欲しい。

順序

デフォルトでは、エクスペクテーションはどのような順序であってもよい。例えば、

m expects (1)
m expects (2)
m(2)
m(1)

特定の順序はinSequenceによって強制することができる。

inSequence {
  m expects (1)
  m expects (2)
}
m(2) // throws ExpectationException
m(1)

複数のシーケンスも指定することができる。 それぞれのシーケンスにおいて正しい順序である限り、異なるシーケンスの順序はまぜこぜでよい。 例えば、

val m1 = mock[Turtle]
val m2 = mock[Turtle]

inSequence {
  m1 expects 'setPosition withArguments (0.0, 0.0)
  m1 expects 'penDown
  m1 expects 'forward withArguments (10.0)
  m1 expects 'penUp
}
inSequence {
  m2 expects 'setPosition withArguments(1.0, 1.0)
  m2 expects 'turn withArguments (90.0)
  m2 expects 'forward withArguments (1.0)
  m2 expects 'getPosition returning (2.0, 1.0)
}

m2.setPosition(1.0, 1.0)
m1.setPosition(0.0, 0.0)
m1.penDown
m2.turn(90.0)
m1.forward(10.0)
m2.forward(1.0)
m1.penUp
expect((2.0, 1.0)) { m2.getPosition }

デバッグ

失敗するエクスペクテーションのデバッグが困難であれば、「VerboseErrors」あるいは「CallLogging」のいずれかあるいは両方をミックスしてみてほしい。

class MyTest extends Suite with MockFactory with VerboseErrors with CallLogging