Scala testing mocking implicit parameters?
Asked Answered
U

1

12

I'm having a bit of a tough time trying to understand how to write tests in Scala when implicit parameters are involved.

I have the following (short version) of my code and test:

Implementation (Scala 2.10, Spray and Akka):

import spray.httpx.SprayJsonSupport._
import com.acme.ResultJsonFormat._

case class PerRequestIndexingActor(ctx: RequestContext) extends Actor with ActorLogging {
  def receive = LoggingReceive {
    case AddToIndexRequestCompleted(result) =>
      ctx.complete(result)
      context.stop(self)
  }
}


object ResultJsonFormat extends DefaultJsonProtocol {
  implicit val resultFormat = jsonFormat2(Result)
}

case class Result(code: Int, message: String)

Test (Using ScalaTest and Mockito):

"Per Request Indexing Actor" should {
    "send the HTTP Response when AddToIndexRequestCompleted message is received" in {
      val request = mock[RequestContext]
      val result = mock[Result]

      val perRequestIndexingActor = TestActorRef(Props(new PerRequestIndexingActor(request)))
      perRequestIndexingActor ! AddToIndexRequestCompleted(result)

      verify(request).complete(result)
    }
  }

This line, verify(request).complete(result) uses an implicit Marshaller to turn Result into JSON.

I can bring a marshaller in to scope by adding implicit val marshaller: Marshaller[Result] = mock[Marshaller[Result]] but when I run the test a different instance of Marshaller is used, so the verification fails.

Even explicitly passing the mock Marshaller to complete fails.

So, can any one advise how to create a mock object for an implicit parameter and make sure that instance is the one used?

Usurer answered 12/5, 2013 at 12:30 Comment(1)
How does explicitly passing the mock fail? Why would you want to use a mock marshaller in the first place? (I haven't used mockito so please excuse me if those are stupid questions)Altimetry
S
5

This is a perfect situation to use a Matcher from Mockito for the marshaller arg. You should not need to mock out the implicit marshaller. All you really want to do is verify that complete was called with a result matching what you expected and also some instance of the marshaller. First, if you haven't already done it, bring the Mockito matchers into scope with an import like so:

import org.mockito.Matchers._

Then, if you wanted reference matching on the result, you could verify like so:

verify(request).complete(same(result))(any[classOf[Marshaller[Result]]])

Or, if you wanted equals matching on result you could do:

verify(request).complete(eq(result))(any(classOf[Marshaller[Result]]))

The trick with matchers is that once you use one for one arg, you have to use them for all args, so that's why we have to use one for result too.

Stellite answered 12/5, 2013 at 23:34 Comment(3)
I had tried that too. Though I had not tried using the same() matcher. I'm not sure why (yet) the test passes when using verify(request).complete(same(result))(any[classOf[Marshaller[Result]]]) but does not pass when using verify(request).complete(eq(result))(any(classOf[Marshaller[Result]])). Thanks for your suggestion though, it helped :)Usurer
Because the request variable is itself a mock, and the eq matcher uses the equals method which has not been stubbed, you always get false thus no match. Same is a good fit here. If request was not a mock and instead some case class, eq might fit betterStellite
Thanks for the follow up. Apologies for not being clear. Using eq the test doesn't even compile. When I use eq the error message is "Type mismatch, expected: Marshaller[Boolean], actual: Marshaller[Result]". And it's an error on any(classOf[Marshaller[Result]]). I'm not 100% clear on why the error occurs? It's pretty odd.Usurer

© 2022 - 2024 — McMap. All rights reserved.