Question

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:

  • Class under test calls a collaborator with an argument
  • Class under test constructs a new object to pass to collaborator
  • Mockito verifies the call was made if the passed object has a specific property set

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?

Was it helpful?

Solution

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).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top