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
Matcherclass encapsulating the functionality of thebeCloseTomatcher:This class allows to use the syntax you're after:
Now, let's see how to reuse the
beCloseTomatcher in theapplymethod:In the code above, we apply a function returning a
MatcherResultto a sequence of values:Note that:
fis anExpectable[A => Double]so we need to take its actualvalueto be able to use itsimilarly we can only apply an
Expectable[T]to aMatcher[T]so we need to use the methodtheValueto transformf.value(v)to anExpectable[Double](from theMustExpectationstrait)Finally, we when have the result of the
forallmatching, we can customize the result messages by using:the inherited
resultmethod building aMatchResult(what theapplymethod of anyMatchershould returnpassing it a boolean saying if the execution of
beCloseTowas successful:.isSuccesspassing it nicely formatted "ok" and "ko" messages, based on the input and on the result message of the
beCloseTomatchingpassing it the
Expectablewhich 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
beCloseTowithforall.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
foreachinstead offorallif 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: