0

I have a function in Scala as follows

object MyService extends MyTrait {    
  def myMethod[T <% InvokableBuilder[MyClass]](builder: T): MyResponse = {
  //do something
  }
}

I'm trying to mock this function using mockito like the following

val mockService = mock[MyTrait]

doReturn(info).when(mockService).myMethod(any())

I get the following error though I have only one argument in the function

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Invalid use of argument matchers! [info] 2 matchers expected, 1 recorded:

2 Answers2

1

Your problem is: scala is not java.

Your implicit assumption is: "that little scala method there gets translated into something similar in Java; and thus I can simply use mockito to deal with that".

Wrong. You are creating a scala object definition here; and if I remember correctly; object in scala ... translates to static in Java (see here for example).

Thus, on a first glance, you probably need to turn to PowerMock(ito) resp. JMockit in order to mock those static elements. (and my usual warning: don't use PowerMock; because mocking static stuff is a bad idea). And as Philipp M is pointing out in his comment: mocking static is really regarded bad practice. You should rather looking into mocking the "trait" side of things here.

So the real answer is: you have to know what you are doing. Mockito is written for java. You can't just assume that anything that you write down in scala and that "looks somehow" like Java can easily be mapped to the concepts that Mockito is doing its work on.

In order to really understand what is going on; you should first have a look at the class files that the scala compiler creates in your case; check out the method signature; and think for yourself: "if I had to call that method in java source code, how would I do that?" and work from there.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • I agree with the first half of that answer - Scala `object`s are singletons and should be treated as such. What I don't like is the idea of using PowerMock to work around that and manipulate bytecode during test execution. In my opinion it would be much better to have the `object` implement a `trait` as you already have, and mock the trait instead. – Philipp Apr 24 '17 at 09:03
  • sorry, my response was half written, and enter submitted it :) – Philipp Apr 24 '17 at 09:05
  • Got it ... and added that to the answer. And I totally agree. – GhostCat Apr 24 '17 at 09:07
  • @Philipp M : I did mock the trait and not the object itself. I updated the code to reflect that – Meghashyam Sandeep V Apr 24 '17 at 14:02
0

I changed your // do something to ??? so it would compile, then printed the code at the end of the parser phase:

$scalac MyService.scala -Xprint:parser
[[syntax trees at end of                    parser]] // MyService.scala
package <empty> {
  object MyService extends MyTrait {
    def <init>() = {
      super.<init>();
      ()
    };
    def myMethod[T](builder: T)(implicit evidence$1:_root_.scala.Function1[T,InvokableBuilder[MyClass]]): MyResponse = $qmark$qmark$qmark
  }
}

As you can see, the myMethod has a second parameter list due to your view bound. I am not sure how you would mock that with Mockito, but I suggest giving ScalaMock a try.

Note: View bounds are deprecated - I'd suggest replacing them with an implicit parameter (see how the scalac parser does that above).

Longish example:

import org.scalamock.scalatest.MockFactory
import org.scalatest.FlatSpec

import scala.language.implicitConversions

class FooTest extends FlatSpec with MockFactory {

  trait MyTrait {
    def myMethod[T](builder: T)(implicit ev$1: T => InvokableBuilder[MyClass]): MyResponse
  }

  trait InvokableBuilder[T]

  class MyClass

  class MyResponse

  class Foo

  object MyService extends MyTrait {
    def myMethod[T](builder: T)(implicit ev$1: T => InvokableBuilder[MyClass]): MyResponse = {
      //do something
      ???
    }
  }

  behavior of "Foo"

  it should "foo" in {
    val x = mock[MyTrait]

    implicit val fooConvert: Foo => InvokableBuilder[MyClass] = ???
    (x.myMethod(_: Foo)).expects(*).once()
  }

}
Philipp
  • 967
  • 6
  • 16