I have functions A => Double
. I want to check whether two such functions give the same results (up to a tolerance, using the existing beCloseTo
matcher) for a given set of values.
I want to be able to write:
type TF = A => Double
(f: TF) must computeSameResultsAs(g: TF,tolerance: Double, tests: Set[A])
I want to build this matcher in a modular way, not simply writing a Matcher[TF]
from scratch.
It might be even nicer if I could write:
(f: TF) must computeSameResultsAs(g: TF)
.withTolerance(tolerance)
.onValues(tests: Set[A])
Also I want to get a reasonable description when the matcher fails.
Edit
After sleeping over it I came up with the following.
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, args: Set[A]): Matcher[A => Double] =
args.map(beCloseOnArg(ref, tolerance, _)).reduce(_ and _)
def beCloseOnArg[A](ref: A => Double, tolerance: Double, arg: A): Matcher[A => Double] =
closeTo(ref(arg), tolerance) ^^ ((_: A => Double).apply(arg))
This is much shorter than Eric's solution but doesn't provide a good failure message. What I'd love to be able is rename the mapped value in the second method. Something like the following (which does not compile).
def beCloseOnArg[A](ref: A => Double, tolerance: Double, arg: A): Matcher[A => Double] =
closeTo(ref(arg), tolerance) ^^ ((_: A => Double).apply(arg) aka "result on argument " + arg)
If you want to write things with the second version you need to create a new
Matcher
class encapsulating the functionality of thebeCloseTo
matcher:This class allows to use the syntax you're after:
Now, let's see how to reuse the
beCloseTo
matcher in theapply
method:In the code above, we apply a function returning a
MatcherResult
to a sequence of values:Note that:
f
is anExpectable[A => Double]
so we need to take its actualvalue
to be able to use itsimilarly we can only apply an
Expectable[T]
to aMatcher[T]
so we need to use the methodtheValue
to transformf.value(v)
to anExpectable[Double]
(from theMustExpectations
trait)Finally, we when have the result of the
forall
matching, we can customize the result messages by using:the inherited
result
method building aMatchResult
(what theapply
method of anyMatcher
should returnpassing 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
matchingpassing it the
Expectable
which was used to do the matching in the first place:f
, so that the final result has a type ofMatchResult[A => Double]
I'm not sure how more modular we can get given your requirements. It looks to me that the best we can do here is to reuse
beCloseTo
withforall
.UPDATE
A shorter answer might be something like this:
The code above creates a failure message like:
This should almost work out-of-the-box. The missing part is an implicit conversion from
A => MatchResult[_]
toMatcher[A]
(which I'm going to add to the next version):You can use
foreach
instead offorall
if you want to get all the failures:UPDATE 2
This gets better everyday. With the latest specs2 snapshot you can write:
UPDATE 3
And now with the latest specs2 snapshot you can write:
The failure message will be: