Mockito argument matchers InvalidUseOfMatchersException for Scala Project - scala

I have this Scala project that I am using mockito (mockito-core 3.0) to test
Here's the function signature of the function I am trying to mock
def hset[V: ByteStringSerializer](key: String, field: String, value: V): Future[Boolean] = ...
This doesnt work
verify(mockObj, never()).hset(anyString(), anyString(), anyLong())
Error outs with this
Invalid use of argument matchers!
4 matchers expected, 3 recorded:
Not sure why its expecting 4 matchers when the function has 3 argument with a generics type
This works
verify(mockObj, never()).hset("a", "b", 3.0)
is this because I am using scala code which doesnt operate correctly with the mockito core ?

The reason for the problem is context bound
def hset[V: ByteStringSerializer](key: String, field: String, value: V): Future[Boolean]
is actually
def hset[V](key: String, field: String, value: V)(implicit ev: ByteStringSerializer[V]): Future[Boolean]
Now you can see why there are 4 arguments, try
verify(mockObj, never()).hset(anyString(), anyString(), anyLong())(any(classOf[ByteStringSerializer[Long]]))

As Ivan pointed out, you're missing the matcher for the implicit.
I'd suggest you to migrate to mockito-scala as this kind of scenarios will work out-of-the-box when the implicit is in scope

Related

Scala Implicit Parameters Projection Conflict , "Ambigious Implicit Values" Error

I have been reading Bruno's TypeClasses paper and he mentioned that implicits in the argument list are projected/propagated into the implicit scope. I followed the example with this code:
package theory
import cats.implicits.toShow
import java.io.PrintStream
import java.util.Date
object Equality extends App {
import cats.Eq
// assert(123 == "123")
println(Eq.eqv(123, 123).show)
implicit val out: PrintStream = System.out
def log(m: String)(implicit o: PrintStream ): Unit =
o.println(m)
def logTime(m: String)(implicit o: PrintStream): Unit =
log(s"${new Date().getTime} : $m")
}
The kicker is that this code will not compile, with:
ambiguous implicit values:
both value out in object Equality of type java.io.PrintStream
and value o of type java.io.PrintStream
match expected type java.io.PrintStream
log(s"${new Date().getTime} : $m")
So, I assume that the compiler sees 2 instances of the same implicit and complains. I was able to silence the compiler by explicitly adding the PrintStream passed as an argument as the second argument to log:
def logTime(m: String)(implicit o: PrintStream): Unit =
log(s"${new Date().getTime} : $m")(o)
This works, but am I missing something? Why is there confusion inside the body of logTime()? I thought Bruno was implying that the implicit from the caller would be projected into the scope of the method. His example does not add the extra parameter to the log() call. Why does scalac see these as 2? I suppose I assumed that the implicit from the outer method would "hide" the val out. Not so.
If anyone can explain why I see this, it would be appreciated.
Recall that the value of an implicit parameter is determined at the call site. That's why...
Equality.log("log this")
...won't compile unless an implicit value of the appropriate type is brought into scope.
implicit val ps: PrintStream = ...
Equality.log("log this")
The logTime() definition code is a call site for the log() method and, since it is defined within the Equality object, it has the implicit val out value available to it. But it is also the recipient of the implicit o value of the same type that was passed from its call site. Thus the ambiguity. Should the compiler send the implicit out value or the implicit o value to the log() method?
Now, it might seem a bit odd that the received implicit value (from the call site) is both assigned to a local identifier, o, and inserted into the local implicit namespace as well. It turns out that Scala-3 has modified that behavior and your code compiles without error, even without the new given/using syntax. (I assume the implicit out value is passed to the log() method and not the received o value.)

How to get Scala case class fields and values as (String, String) with Shapeless or Macro

I have been struggling for a couple days already to try to create a macro or use shapeless to create a method/function to extract field names and values as a Tuple[String, String].
Lets imagine the following case class:
case class Person(name: String, age: Int)
I want to have something like this (doesn't really need to be a method in case class).
case class Person(name: String, age: Int) {
def fields: List[(String, String)] = ???
}
// or
def fields[T](caseClass: T): List[(String, String)] = ???
I've seen quite few similar solutions here but I can't make it work with my use case of (String, String)
I would also appreciate some literature to learn and expand my knowledge regarding macros, I have both Programming in Scala(Third Edition by Martin) and Programming Scala (O'REILLY - Dean Wampler & Alex Payne) and only O'REILLY has a very small chapter regarding macros and to be honest its very lacking.
Thank you!
PD: I'm using Scala 2.12.12 so I don't have those fancy new methods for case class productElementNames and such :(
Based on LabelledGeneric and Keys type classes
import shapeless.LabelledGeneric
import shapeless.HList
import shapeless.ops.hlist.ToTraversable
import shapeless.ops.record.Keys
case class Person(name: String, age: Int)
def fields[P <: Product, L <: HList, R <: HList](a: P)(
implicit
gen: LabelledGeneric.Aux[P, L],
keys: Keys.Aux[L, R],
ts: ToTraversable.Aux[R, List, Symbol]
): List[(String, String)] = {
val fieldNames = keys().toList.map(_.name)
val values = a.productIterator.toList.map(_.toString)
fieldNames zip values
}
fields(Person("Jean-Luc, Picard", 70))
// : List[(String, String)] = List((name,Jean-Luc, Picard), (age,70))
scastie
IDEA ... shows an error ... No implicit arguments
IntelliJ in-editor error highlighting is sometimes not 100% accurate when it comes to type-level code and macros. Best is to consider it as just guidance, and put trust in the Scala compiler proper, so if compiler is happy but IJ is not, then go with the compiler. Another options is to try Scala Metals which should have one-to-one mapping between compiler diagnostics and in-editor error highlighting.
why you used LabelledGeneric.Aux, Keys.Aux, ToTraversable.Aux
This is using a design pattern called type classes. My suggestion would be to work through The Type Astronaut's Guide to Shapeless in particular section on Chaining dependent functions
Dependently typed functions provide a means of calculating one type
from another. We can chain dependently typed functions to perform
calculations involving multiple steps.
Consider the following dependency between types
input type
|
gen: LabelledGeneric.Aux[P, L],
|
output type
input type
|
keys: Keys.Aux[L, R]
|
output type
Note how for example the output type L of LabelledGeneric becomes the input type of Keys. In this way you are showing the compiler the relationship between the types and in return the compiler is able to give your an HList representing the field names from Product representing the particular case class, and all this before the program even runs.
ToTraversable is needed so you can get back a regular Scala List from an HList which enables the following bit
.toList.map(_.name)
Hopefully this gives you at least a little bit of direction. Some keywords to search for are: type classes, dependent types, implicit resolution, type alias Aux pattern, type members vs type parameters, type refinement, etc. Typelevel community has a new Discord channel where you can get further direction.

SBT confused about Scala types

SBT is throwing the following error:
value split is not a member of (String, String)
[error] .filter(arg => arg.split(delimiter).length >= 2)
For the following code block:
implicit def argsToMap(args: Array[String]): Map[String, String] = {
val delimiter = "="
args
.filter(arg => arg.split(delimiter).length >= 2)
.map(arg => arg.split(delimiter)(0) -> arg.split(delimiter)(1))
.toMap
}
Can anyone explain what might be going on here?
Some details:
java version "1.8.0_191"
sbt version 1.2.7
scala version 2.11.8
I've tried both on the command line and also with intellij. I've also tried Java 11 and Scala 2.11.12 to no avail.
I'm not able to replicate this on another machine (different OS, SBT, IntelliJ, etc. though) and I can also write a minimal failing case:
value split is not a member of (String, String)
[error] Array("a", "b").map(x => x.split("y"))
The issue is that the filter method is added to arrays via an implicit.
When you call args.filter(...), args is converted to ArrayOps via the Predef.refArrayOps implicit method.
You are defining a implicit conversion from Array[String] to Map[(String, String)].
This implicit has higher priority than Predef.refArrayOps and is therefore used instead.
So args is converted into a Map[(String, String)]. The filter method of that Map would expect a function of type (String, String) => Boolean as parameter.
I believe what happened is that the implicit method is getting invoked a bit too eagerly. That is, the Tuple2 that's seemingly coming out of nowhere is the result of the implicit function converting each String into a key/value pair. The implicit function was recursively calling itself. I found this out after eventually getting a stack overflow with some other code that was manipulating a collection of Strings.

How to mock scala generic method

I have a traits(below simple versions) and I want to mock Foo.
trait MyTrait[A]
trait Foo {
def bar[T: MyTrait](id:Int, data: T, other:Option[String] = None): String
}
I tried:
implicit val myTrait = new MyTrait[String] {}
val src = mock[Foo]
when(src.bar(any(),any(),any())).thenReturn("ok")
src.bar(1, "some", None)
It fails with:
Invalid use of argument matchers! 4 matchers expected, 3 recorded
How to mock this kind of method?
Mockito does not play well with scala code. What happens in your case is that
notify.bar(any(), any(), any()) has to be invoked inside when(), but type of the bar method is unknown, so when scalac is looking-up implicits there are possibly several instances of Writes that fit here (becuase all of them do).
You can do something like this to make it work:
when(src.bar[T](any(),any(),any())).thenReturn("ok")
Edit
Following your edit, i think you should reconsider usage of mockito in the first place. Here's what happens:
Foo has the following signature after desugaring
trait Foo {
def bar[T](id:Int, data: T, other:Option[String] = None)(implicit ev: MyTrait[T]): String
}
I don't know if you are aware how does mockito work, but here is a quick explanation why (from what i can tell) this error happens:
at runtime method bar has the following "signature" (due to type erasure):
bar(id: Int, data: Object, other: Option[Object], ev: MyTrait[Object])
when(src.bar[T](any(),any(),any())).thenReturn("ok") actually invokes this method on a proxy object and registers "matchers", but ev is passed myTrait instead of a matcher, so i guess this violates some constraints of the library
As side-note: usually mocking is not that hard and you can simply implement a "mocked" trait without any help from mockito or other similar library.
The syntax [T : MyTrait] is sugar to add (implicit typeClass: MyTrait[T]) as a second set of arguments, so basically you're missing an argument matcher as the error states
So if you do
val src = mock[Foo]
when(src.bar(any(),any(),any())(any())).thenReturn("ok")
it works as expected
BTW, this is a bit of self promotion but I just published a library called mockito-scala that improves the mockito syntax for Scala, is part of the mockito ecosystem so hopefully should become the default when working with Scala, you can find it here https://github.com/mockito/mockito-scala with the information to get the dependency and what problems does it actually solves.

Scala: Cast to a runtime Class instance

See the following code snippet:
def getObject(key: String, cacheName: String)(clazz: Class[_ <: ICacheable]) = {
Option(icacheFactory.getCache(cacheName).get(key).asInstanceOf[clazz])
}
The compiler complains about the asInstanceOf[clazz]
Basically what I am trying to do is to cast the ICacheable returned from icacheFactory.getCache(cacheName).get(key) to an instance of clazz
How do I go about this? It seems that asInstanceOf only supports static types
The closest to your solution is
def getObject(key: String, cacheName: String)(clazz: Class[_ <: ICacheable]) =
Try(clazz.cast(icacheFactory.getCache(cacheName).get(key)))
(see Class javadoc for cast). If you know the class you want at the compilation time instead of receiving it from outside (e.g. as string or as Class from client code), you can avoid passing the classes explicitly by using ClassTags:
def getObject[T <: ICacheable](key: String, cacheName: String)(implicit tag: ClassTag[T]) =
Try(tag.runtimeClass.cast(icacheFactory.getCache(cacheName).get(key)))
You call it as follows:
getObject[SomeCacheable](key, cacheName)
For the first version:
getObject(key, cacheName)(classOf[SomeCacheable])
Straight brackets, "[]", indicate a type parameter - you must supply asInstanceOf with a type, not a value like clazz. Also, asInstanceOf never returns null, instead it throws an exception if the cast isn't possible, so wrap it in Try rather than Option. Here is a rewritten version of your function:
def getObject[A <: ICacheable](key: String, cacheName: String) = {
Try(icacheFactory.getCache(cacheName).get(key).asInstanceOf[A])
}
Unlike Java code with the hideous need to pass around Class objects, in Scala type inference might make it completely unnecessary to even specify the type parameter:
def f(x: SubclassOfICacheable) = ???
getObject("cache", "key") map f
Scala can infer that A should be SubclassOfICacheable without needing it explicitly specified.