Als u dingen wilt schrijven met de tweede versie, moet u een nieuwe Matcher
-klasse maken met de functionaliteit van de beCloseTo
-overeenkomst:
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)
}
Met deze klasse kunt u de syntaxis gebruiken die u zoekt:
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)
}
Laten we nu kijken hoe we de beCloseTo
-overeenkomst in de apply
-methode opnieuw kunnen gebruiken:
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)
}
In de bovenstaande code passen we een functie toe die een MatcherResult
naar een reeks waarden :
((v: A) => beCloseTo(g(v) +/- tolerance).apply(theValue(f.value(v)))).forall(values)
Let daar op:
f
is an Expectable[A => Double]
so we need to take its actual value
to be able to use it
similarly we can only apply an Expectable[T]
to a Matcher[T]
so we need to use the method theValue
to transform f.value(v)
to an Expectable[Double]
(from the MustExpectations
trait)
Als we uiteindelijk het resultaat van de forall
-overeenkomst hebben, kunnen we de resultaatberichten aanpassen met behulp van:
the inherited result
method building a MatchResult
(what the apply
method of any Matcher
should return
passing it a boolean saying if the execution of beCloseTo
was successful: .isSuccess
passing it nicely formatted "ok" and "ko" messages, based on the input and on the result message of the beCloseTo
matching
passing it the Expectable
which was used to do the matching in the first place: f
, so that the final result has a type of MatchResult[A => Double]
Ik weet niet zeker hoe modulairer we aan uw eisen kunnen komen. Volgens mij is het beste wat we hier kunnen doen, het opnieuw gebruiken van beCloseTo
met forall
.
UPDATE
Een korter antwoord kan zoiets als dit zijn:
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)
}
De bovenstaande code creëert een foutmelding zoals:
In the sequence '1, 2, 3', the 1st element is failing: 1.0 is not close to 2.0 +/- 0.5
This should almost work out-of-the-box. The missing part is an implicit conversion from A => MatchResult[_]
to Matcher[A]
(which I'm going to add to the next version):
implicit def functionResultToMatcher[T](f: T => MatchResult[_]): Matcher[T] = (t: T) => {
val result = f(t)
(result.isSuccess, result.message)
}
U kunt gebruiken foreach
in plaats van forall
als u alle fouten wilt ontvangen:
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
UPDATE 2
Dit wordt elke dag beter. Met de nieuwste momentopname van specs2 kunt u schrijven:
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)
}
UPDATE 3
En nu met de nieuwste momentopname van specs2 kunt u schrijven:
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)
}
Het foutbericht zal zijn:
In the sequence '1, 2, 3', the 1st element is failing: the value '1.0' is not close to 2.0 +/- 0.5