2番目のバージョンで物事を書きたい場合は、マッチャーMatcher
の機能をカプセル化する新しいクラスを作成する必要があります。beCloseTo
def computeSameResultsAs[A](g: A => Double,
tolerance: Double = 0.0,
values: Seq[A] = Seq()) = TFMatcher(g, tolerance, values)
case class TFMatcher[A](g: A => Double,
tolerance: Double = 0.0,
values: Seq[A] = Seq()) extends Matcher[A => Double] {
def apply[S <: A => Double](f: Expectable[S]) = {
// see definition below
}
def withTolerance(t: Double) = TFMatcher(g, t, values)
def onValues(tests: A*) = TFMatcher(g, tolerance, tests)
}
このクラスでは、目的の構文を使用できます。
val f = (i: Int) => i.toDouble
val g = (i: Int) => i.toDouble + 0.1
"f must be close to another similar function with a tolerance" in {
f must computeSameResultsAs[Int](g).withTolerance(0.5).onValues(1, 2, 3)
}
それでは、メソッドでbeCloseTo
マッチャーを再利用する方法を見てみましょう。apply
def apply[S <: A => Double](f: Expectable[S]) = {
val res = ((v: A) => beCloseTo(g(v) +/- tolerance).apply(theValue(f.value(v)))).forall(values)
val message = "f is "+(if (res.isSuccess) "" else "not ")+
"close to g with a tolerance of "+tolerance+" "+
"on values "+values.mkString(",")+": "+res.message
result(res.isSuccess, message, message, f)
}
MatcherResult
上記のコードでは、値のシーケンスにaを返す関数を適用します。
((v: A) => beCloseTo(g(v) +/- tolerance).apply(theValue(f.value(v)))).forall(values)
ご了承ください:
f
ですExpectable[A => Double]
ので、実際value
に使用できるようにする必要があります
Expectable[T]
同様に、にのみ適用できるMatcher[T]
ので、メソッドを使用して(トレイトから)theValue
に変換する必要がありますf.value(v)
Expectable[Double]
MustExpectations
最後に、forall
マッチングの結果が得られたら、次を使用して結果メッセージをカスタマイズできます。
継承されたresult
メソッドの構築MatchResult
(apply
いずれかのメソッドがMatcher
返す必要があるもの
beCloseTo
の実行が成功したかどうかを言うブール値を渡します。.isSuccess
beCloseTo
入力とマッチングの結果メッセージに基づいて、適切にフォーマットされた「ok」メッセージと「ko」メッセージを渡します。
Expectable
そもそもマッチングを行うために使用されたものを渡すとf
、最終結果は次のタイプになります。MatchResult[A => Double]
あなたの要件を考慮して、どれだけモジュール化できるかわかりません。ここでできる最善のことは、で再利用することだと私には思えbeCloseTo
ますforall
。
アップデート
短い答えは次のようになります。
val f = (i: Int) => i.toDouble
val g = (i: Int) => i.toDouble + 1.0
"f must be close to another similar function with a tolerance" in {
f must computeSameResultsAs[Int](g, tolerance = 0.5, values = Seq(1, 2, 3))
}
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, values: Seq[A]): Matcher[A => Double] = (f: A => Double) => {
verifyFunction((a: A) => (beCloseTo(ref(a) +/- tolerance)).apply(theValue(f(a)))).forall(values)
}
上記のコードは、次のような失敗メッセージを作成します。
In the sequence '1, 2, 3', the 1st element is failing: 1.0 is not close to 2.0 +/- 0.5
これは、ほとんどそのままで機能するはずです。A => MatchResult[_]
欠落している部分は、からへの暗黙の変換Matcher[A]
です(これは次のバージョンに追加します)。
implicit def functionResultToMatcher[T](f: T => MatchResult[_]): Matcher[T] = (t: T) => {
val result = f(t)
(result.isSuccess, result.message)
}
すべての失敗を取得したい場合は、foreach
代わりにを使用できます。forall
1.0 is not close to 2.0 +/- 0.5; 2.0 is not close to 3.0 +/- 0.5; 3.0 is not close to 4.0 +/- 0.5
更新2
これは毎日良くなります。最新のspecs2スナップショットを使用すると、次のように記述できます。
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, values: Seq[A]): Matcher[A => Double] = (f: A => Double) => {
((a: A) => beCloseTo(ref(a) +/- tolerance) ^^ f).forall(values)
}
更新3
そして今、最新のspecs2スナップショットを使用して、次のように記述できます。
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, values: Seq[A]): Matcher[A => Double] = (f: A => Double) => {
((a: A) => beCloseTo(ref(a) +/- tolerance) ^^ ((a1: A) => f(a) aka "the value")).forall(values)
}
失敗メッセージは次のようになります。
In the sequence '1, 2, 3', the 1st element is failing: the value '1.0' is not close to 2.0 +/- 0.5