I'm trying to test that when a method call is received on a collaborator Mockito will accept the verification if only a certain property is correctly set. So logically this is:
Code wise what I have so far is:
class ExampleSpec extends Specification with Mockito with Hamcrest {
val collaborator = mock[TargetObject]
"verifying a mock was called" should {
"match if a field on the called parameter was the same" in {
val cut = new ClassUnderTest(collaborator)
cut.execute();
there was one(collaborator).call(anArgThat(hasProperty("first", CoreMatchers.equalTo("first"))))
}
}
}
Where the classes defined are:
class ClassUnderTest(collaborator: TargetObject) {
def execute() =
collaborator.call(new FirstParameter("first", "second"))
}
class FirstParameter(val first: String, val second: String) {
}
trait TargetObject {
def call(parameters: FirstParameter)
}
In vanilla Java I would accomplish this either with a Hamcrest hasProperty matcher (as tried above) or by implementing my own FeatureMatcher to extract the field I want. The code above errors with the following:
java.lang.Exception: The mock was not called as expected:
Argument(s) are different! Wanted:
targetObject.call(
hasProperty("first", "first")
);
-> at example.ExampleSpec$$anonfun$1$$anonfun$apply$2$$anonfun$apply$1.apply$mcV$sp(ExampleSpec.scala:18)
Actual invocation has different arguments:
targetObject.call(
FirstParameter(first,second)
);
The diagnostics aren't really telling me much. Is there a way to do this how I want with the Hamcrest matchers, or ideally a more idiomatic way to do this with Specs2?
The idiomatic way would be to use the MatcherMacros
trait (in specs2-matcher-extra 2.3.10
)
import org.specs2.mock.Mockito
import org.specs2.matcher._
class TestSpec extends org.specs2.mutable.Specification with Mockito with MatcherMacros {
val collaborator = mock[TargetObject]
"verifying a mock was called" should {
"match if a field on the called parameter was the same" in {
val cut = new ClassUnderTest(collaborator)
cut.execute
there was one(collaborator).call(matchA[FirstParameter].first("first"))
}
}
}
class ClassUnderTest(collaborator: TargetObject) {
def execute = collaborator.call(FirstParameter("first", "second"))
}
case class FirstParameter(first: String, second: String)
trait TargetObject {
def call(parameters: FirstParameter)
}
In the code above matchA
is used to match a FirstParameter
and the first
method on matchA
corresponds to the first
value of the FirstParameter
class. In this example I just pass the expected value but you can also pass another specs2 matcher, startWith("f")
for example, or even a function (s: String) => s must haveSize(5)
.