I'm trying to verify the following method gets called using Mockito:
class Notifier {
def forward(request: ServletRequest)(onFailure: => Unit) : Unit
}
Here's the verification on a mock:
val notifier = mock[Notifier]
there was one(notifier).forward(any[ServletRequest])(any[() => Unit])
And I get the exception:
The mock was not called as expected:
Invalid use of argument matchers!
3 matchers expected, 2 recorded.
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
I know this is caused by the last parameterless function. How can I perform a verify properly here?
Could you try Function0[Unit] ?
there was one(notifier).forward(any[ServletRequest])(any[Function0[Unit]])
Related
I have a method with this signature:
def foo(param1: => String, param2: (String, String)*)(implicit param3: Context): Unit
In my code, I call it as
foo("bar") // no varargs, implicit is in scope
In my Unit test, I am trying to verify the call:
verify(mock).foo(stringCaptor.capture())(any[Context])
This compiles, but produces a runtime exception:
Invalid use of argument matchers!
3 matchers expected, 1
recorded:
-> at
com.mycompany.MySpec$$anon$3.(MySpec.scala:88)
This
exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When
using matchers, all arguments have to be provided by
matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more
info see javadoc for Matchers class.
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers! 3 matchers expected, 1 recorded:
Yet if I try to match the varargs parameter, I get compilation errors:
verify(mock).foo(stringCaptor.capture(), any[Seq[(String, String)]])(any[Context])
Cannot resolve overloaded method 'foo'
What would be the syntax to match my method and verify the call correctly?
Note: answers to other questions suggest adding a dependency to mockito-scala, which I'd like to avoid
Update: adding a matcher for a tuple reduces the missing matchers from 1 to 2:
verify(mock).foo(stringCaptor.capture(),any[(String, String)])(any[Context])
Invalid use of argument matchers!
3 matchers expected, 2
recorded:
[...]
(it doesn't make a difference if I replace the ArgumentCaptor with anyString())
Another Update:
Ah, it seems the => String part causes the problem, didn't realize that I'm trying to match a lazy string here. Under the hoods, the compiler seems to turn my String into a Function0[String], but how can I match that?
I was trying to find out if I can use something like the any argument matcher for a method with type parameters:
when(store.getItems[Product](any[FilterParams]))
.thenReturn(allProducts)
When running the code with the above snippet, I get this error:
Invalid use of argument matchers!
2 matchers expected, 1 recorded
I guessed this would be because I passed an exact type parameter Product to the getItems method.
This does work when I pass in an actual FilterParams object:
when(store.getItems[Product](FilterParams()))
.thenReturn(allProducts)
Is there any argument matcher that I can use for the type parameter? Or is it counter-intuitive, because whenever we're calling a function with a type parameter, we have to pass the type parameter?
Edit:
Product here isn't Scala's built-in Product trait.
The function signature of the getItems method:
def getItems[T <: ItemType : universe.TypeTag](filterParams: FilterParams): List[T]
I have a method with the following signature:
def fetchCode[T](
seconds: Int,
client: String,
scope: String,
data: T,
retryLimit: Int = 10
)(implicit formats: Formats): String
and in my tests I'm trying to mock it as:
val accessCode: String = "CODE"
when(
mockService
.fetchCode[String](
any[Int],
any[String],
Matchers.eq(partner.name),
any[String],
any[Int]
)
).thenReturn(accessCode)
verify(mockService).fetchCode(
Matchers.any(),
Matchers.any(),
Matchers.eq(partner.name),
Matchers.any(),
Matchers.any()
)
Upon running this test, I still see the following errors:
Invalid use of argument matchers!
6 matchers expected, 5 recorded:
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more info see javadoc for Matchers class.
I don't see why this error crops up - I only need 5 matchers - one each for an argument, why are 6 expected?
As #Levi mentioned in his answer, you need to address all arguments the method gets, in order to use mocks. As you can see as part of your error message:
6 matchers expected, 5 recorded
What you need to do is to add any[Formats] in new paranthesis (exactly like your original method), and provide their the mock value:
when(
mockService
.fetchCode[String](
any[Int],
any[String],
Matchers.eq(partner.name),
any[String],
any[Int]
)(any[Formats])
).thenReturn(accessCode)
verify(mockService).fetchCode(
Matchers.any(),
Matchers.any(),
Matchers.eq(partner.name),
Matchers.any(),
Matchers.any()
)(any[Formats])
implicit formats: Formats is also passed as an argument, so mockito will need to be able to match it.
I have the folliwng PlaySpec:
"Service A" must {
"do the following" in {
val mockServiceA = mock[ServiceA]
val mockServiceB = mock[ServiceB]
when(mockServiceA.applyRewrite(any[ClassA])).thenReturn(resultA) // case A
when(mockServiceB.execute(any[ClassA])).thenReturn(Future{resultB})
// test code continuation
}
}
The definition of ServiveA and ServiceB are
class ServiceA {
def applyRewrite(instance: ClassA):ClassA = ???
}
class ServiceB {
def execute(instance: ClassA, limit: Option[Int] = Some(3)) = ???
}
Mocking ServiceA#applyRewrite works perfectly.
Mocking ServiceB#execute fails with the following exception:
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at RandomServiceSpec.$anonfun$new$12(RandomServiceSpec.scala:146)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
Although the instructions included in the exception seem a bit counterintuitive to me I have tried the following:
when(mockServiceB.execute(anyObject[ClassA])).thenReturn(Future{resultB})
when(mockServiceB.execute(anyObject())).thenReturn(Future{resultB})
when(mockServiceB.execute(anyObject)).thenReturn(Future{resultB})
when(mockServiceB.execute(any)).thenReturn(Future{resultB})
when(mockServiceB.execute(any, Some(3))).thenReturn(Future{resultB})
when(mockServiceB.execute(any[ClassA], Some(3))).thenReturn(Future{resultB})
All unfortunately to no avail. The only thing that changes is the number of expected and recorded matchers the exception refers to.
The weirdest thing for me though is that the mocking works perfectly for case A.
Use the idiomatic syntax of mockito-scala and all the stuff related to the default argument will be deal with by the framework
mockServiceB.execute(*) returns Future.sucessful(resultB)
if you add the cats integration it could reduce to just
mockServiceB.execute(*) returnsF resultB
more info here
You need to do this:
import org.mockito.ArgumentMatchersSugar._
when(mockServiceB.execute(any[ClassA], eqTo(Some(3)))).thenReturn(Future{resultB})
When you use any and the function receives multiple arguments you need to pass the other arguments that are not any with eq(something), hope this helps.
EDITED: My bad forgot the import and is eqTo and not eq
Short form: I have a method with the same signature as Future.recover. Passing a partial function to Future's version works. Passing the same PF to my version results in a missing parameter type for expanded function. The argument types of an anonymous function must be fully known. (SLS 8.5) error. What's the difference?
Longer form:
I'm trying to implement the TracingFuture class discussed here in an attempt to trace errors across future boundaries. The basic technique is to wrap the Future in another class, TracingFuture, while adding a pseudo-stacktrace.
The code given in the blog post is missing the recover method from Future, so I've added it with the same signature:
class TracingFuture[+T](underlying: Future[T], val trace: Vector[FutureTraceElement]) extends Future[T] {
def recover[U >: T](pf: PartialFunction[Throwable, U]
)(implicit ec: ExecutionContext, enclosing: sourcecode.Enclosing, file: sourcecode.File,
line: sourcecode.Line): TracingFuture[U] = {
val recovered = underlying.recover(pf)
new TracingFuture[U](recovered, trace :+ FutureTraceElement(enclosing.value, "recover", file.value, line.value))
}
}
For comparison, here's the equivalent chunk of code out of Future. Note that aside from the extra implicit parameters the signatures are the same.
trait Future[+T] extends Awaitable[T] {
def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] =
transform { _ recover pf }
}
Finally, my code that produces the compile error:
val x: TracingFuture[Vector[Maintainer]] = ... // code producing a TracingFuture
val fMaintainers = x.recover {
case err: Throwable ⇒
logger.error("Failed to get list of user maintainers.", err)
Vector.empty[Maintainer]
}
And the error message:
[error] /Users/bwbecker/oat/src/oat3/modules/wapp/app/oat/wapp/dao/CronJobDAO.scala:273: missing parameter type for expanded function
[error] The argument types of an anonymous function must be fully known. (SLS 8.5)
[error] Expected type was: ?
[error] val fMaintainers = x.recover {
[error] ^
Once again, this code works with the Future.recover but I get a compile error with TracingFuture.recover. I don't understand why.
This SO question explains that the compiler knows the argument to the partial function must be a supertype of T but can't guarantee that. But why doesn't it run into that issue with Future.recover?
And, of course, I'd like to know if there's anything I do about it other than rewriting the anonymous partial function to make the types explicit.
The problem is that TracingFuture has two overloaded recover methods: the one you added and the one you inherited from Future. When you only have one, it provides the expected type which is crucial for type inference, but with overloaded methods it doesn't work, as you see from Expected type was: ?.
You may think the compiler should notice types of function parameters are the same and so can still provide the expected type. And you would be right, but it was only fixed in Scala 2.12.
Of course, then you'll run into trouble that the compiler has no way to tell which overload you want when only the implicit arguments are different.
Try to replace
val fMaintainers = x.recover {
case err: Throwable ⇒
logger.error("Failed to get list of user maintainers.", err)
Vector.empty[Maintainer]
}
with
val fMaintainers = x.recover(PartialFunction[Throwable, Vector[Maintainer]] {
case err: Throwable ⇒
logger.error("Failed to get list of user maintainers.", err)
Vector.empty[Maintainer]
})
Why do I get a "missing parameter for expanded function" in one case and not the other?