My application code uses AService
trait AService {
def registerNewUser (username: String)(implicit tenant: Tenant): Future[Response]
}
to register a new user. Class Tenant is a simple case class:
case class Tenant(val vstNumber:String, val divisionNumber:String)
Trait AServiceMock mimics the registration logic by using a mocked version of AService
trait AServiceMock {
def registrationService = {
val service = mock[AService]
service.registerNewUser(anyString) returns Future(fixedResponse)
service
}
}
Iow whenever registerNewUser is called on AService the response will be "fixedResponse" (defined elsewhere).
My question is, how do I define the implicit tenant-parameter as a mockito matcher like anyString?
btw. I'm using Mockito with Specs2 (and Play2)
Matcher methods can't be used as return values; there is no way to phrase thenReturn(anyInt()) or thenReturn(any(Foo. class)) in Mockito, for instance. Mockito needs to know exactly which instance to return in stubbing calls, and will not choose an arbitrary return value for you.
Since Mockito any(Class) and anyInt family matchers perform a type check, thus they won't match null arguments. Instead use the isNull matcher.
Argument matchers are mainly used for performing flexible verification and stubbing in Mockito. It extends ArgumentMatchers class to access all the matcher functions. Mockito uses equal() as a legacy method for verification and matching of argument values.
Sometimes you have to post on SO first to come up with the completely obvious answer (duhh):
service.registerNewUser(anyString)(any[Tenant]) returns Future(fixedResponse)
This a complement to @simou answer. For now I think this is how it should be done, but I think it is intresting to know why the alternative solution proposed by @Enrik should be avoided as it may fail at run time with a cryptic error in some circumstances.
What you can safely do is if you want an exact match on your implicit argument for your stub, you can just add it in the scope :
trait AServiceMock {
implicit val expectedTenant: Tenant = Tenant("some expected parameter")
def registrationService = {
val service = mock[AService]
service.registerNewUser(anyString) returns Future(fixedResponse)
service
}
}
This will work fine but only if service.registerNewUser is expected to be called with the exact same tenant that the one provided by the implicit value expectedTenant .
What will not reliably work on the other hand is anything in the style :
implicit val expectedTenant1: Tenant = any[Tenant]
implicit def expectedTenant2: Tenant = any[Tenant]
implicit def expectedTenant3: Tenant = eqTo(someTenant)
To reason is related to how mockito create its argument matcher.
When you write myFunction(*,12) returns "abc"
mockito actually use a macro that :
In the case of expectedTenant2 or expectedTenant3 what may append is that a first argument matcher will be registerd when the function is evaludated. But the macro will not see this function is registering a macther. It will only consider the declared return type of this function and so may decide to wrap this returned value inside a second matcher.
So in practice if you have code like this
trait AServiceMock {
implicit def expectedTenant(): Tenant = any[Tenant]
def registrationService = {
val service = mock[AService]
service.registerNewUser(anyString) returns Future(fixedResponse)
service
}
}
You expect it to be like that after applying the implicit :
trait AServiceMock {
def registrationService = {
val service = mock[AService]
service.registerNewUser(anyString)(any[Tenant]) returns Future(fixedResponse)
service
}
}
But actually mockito macro will make it as something more or less like that :
trait AServiceMock {
def registrationService = {
val service = mock[AService]
// In practice the macro use DefaultMatcher and not eqTo but that do not change much for the matter we discuss.
service.registerNewUser(anyString)(eqTo(any[Tenant])) returns Future(fixedResponse)
service
}
}
So now you declare two matcher inside the implicit argument of your stub. When mockito will retrive the list of matchers that were declared for registerNewUser, it will see three of them and will think that you are trying to register a stub with three argument for a function that need only two and will log :
Invalid use of argument matchers!
2 matchers expected, 3 recorded:
I'm not yet sure why it may still work in some cases, my hypotheses are :
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With