2

I am using ScalaMock and Mockito

I have this simple code

class MyLibrary {
   def doFoo(id: Long, request: Request) = {
      println("came inside real implementation")
      Response(id, request.name)
   }
}

case class Request(name: String)
case class Response(id: Long, name: String)

I can easily mock it using this code

val lib = new MyLibrary()
val mock = spy(lib)
when(mock.doFoo(1, Request("bar"))).thenReturn(Response(10, "mock"))
val response = mock.doFoo(1, Request("bar"))
response.name should equal("mock")

But If I change my code to

val lib = new MyLibrary()
val mock = spy(lib)
when(mock.doFoo(anyLong(), any[Request])).thenReturn(Response(10, "mock"))
val response = mock.doFoo(1, Request("bar"))
response.name should equal("mock")

I see that it goes inside the real implementation and gets a null pointer exception.

Philipp
  • 967
  • 6
  • 16
Knows Not Much
  • 30,395
  • 60
  • 197
  • 373

2 Answers2

5

I am pretty sure it goes inside the real implementation without matchers too, the difference is that it just doesn't crash in that case (any ends up passing null into the call).

When you write when(mock.doFoo(...)), the compiler has to call mock.doFoo to compute the parameter that is passed to when.

Doing this with mock works, because all implementations are stubbed out, but spy wraps around the actual object, so, the implementations are all real too.

Spies are frowned upon in mockito world, and are considered code smell. If you find yourself having to mock out some functionality of your class while keeping the rest of it, it is almost surely the case when you should just split it into two separate classes. Then you'd be able to just mock the whole "underlying" object entirely, and have no need to spy on things.

If you are still set on using spies for some reason, doReturn would be the workaround, as the other answer suggests. You should not pass null as the vararg parameter though, it changes the semantics of the call. Something like this should work:

 doReturn(Response(10, "mock"), Array.empty:_*).when(mock).doFoo(any(), any())

But, I'll stress it once again: this is just a work around. The correct solution is to use mock instead of spy to begin with.

Dima
  • 39,570
  • 6
  • 44
  • 70
  • 1
    The line ` doReturn(Response(10, "mock"), Array.empty:_*).when(.when(mock.doFoo(any, any)) ` doesn;t compile. I changed it to ` doReturn(Response(10, "mock"), Array.empty:_*).when(mock.doFoo(any, any))` but it still doesn't compile – Knows Not Much Jul 18 '17 at 14:11
  • 1
    There needs to be a star after underscore. It compiles fine for me (after removing the extra .`when` :)) What error are you getting? – Dima Jul 18 '17 at 14:12
  • `doReturn(Response(10, "mock"), Array.empty:_*).when(mock.doFoo(any, any))` throws a bunch of compiler errors `Error:(24, 73) polymorphic expression cannot be instantiated to expected type; found : [T]()T required: Long doReturn(Response(10, "mock"), Array.empty:_*).when(mock.doFoo(any, any))` – Knows Not Much Jul 18 '17 at 14:17
  • 1
    ah ... you need `()` after any. Sorry about that (updating the answer). – Dima Jul 18 '17 at 14:17
  • doesn't work `[info] - should use mock module *** FAILED *** [info] org.mockito.exceptions.misusing.UnfinishedStubbingException: Unfinished stubbing detected here: [info] -> at com.abhi.MyLibTest.$anonfun$new$2(MyLibTest.scala:24) ` – Knows Not Much Jul 18 '17 at 14:20
  • My line is `doReturn(Response(10, "mock"), Array.empty:_*).when(mock.doFoo(any(), any()))` – Knows Not Much Jul 18 '17 at 14:20
  • 1
    Ok, fixed again ... Try it now. But seriously, this is so confusing for a reason. The real solution is just don't use spies. – Dima Jul 18 '17 at 14:26
  • It works! thanks a ton. yes I will refactor my code and eliminate the need to do spies. – Knows Not Much Jul 18 '17 at 14:27
1

Try this

doReturn(Response(10, "mock"), null.asInstanceOf[Array[Object]]: _*).when(mock.doFoo(anyLong(), any[Request]))
talex
  • 17,973
  • 3
  • 29
  • 66
  • 1
    ```Error:(24, 10) ambiguous reference to overloaded definition, both method doReturn in object Mockito of type (x$1: Any, x$2: Object*)org.mockito.stubbing.Stubber and method doReturn in object Mockito of type (x$1: Any)org.mockito.stubbing.Stubber match argument types (com.abhi.Response) doReturn(Response(10, "mock")).when(mock.doFoo(anyLong(), any[Request]))``` – Knows Not Much Jul 18 '17 at 12:12
  • 1
    According to this: https://stackoverflow.com/questions/3313929/how-do-i-disambiguate-in-scala-between-methods-with-vararg-and-without it is known scala problem. Ticket said that they fix it at some point. Do you use latest version of scala? – talex Jul 18 '17 at 12:22
  • I can only use 2.11 as of now. – Knows Not Much Jul 18 '17 at 13:41
  • I changed to ugly version that workaround that problem. – talex Jul 18 '17 at 13:47
  • Its not working. I checked in my code here which incorporates your version above. https://github.com/abhsrivastava/GuiceMockAny – Knows Not Much Jul 18 '17 at 13:57