org.specs2.mock.Mockito matchers are not working as expected - scala

Here is the code I am trying to run:
import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import org.specs2.specification.Scope
import akka.event.LoggingAdapter
class MySpec extends Specification with Mockito {
"Something" should {
"do something" in new Scope {
val logger = mock[LoggingAdapter]
val myVar = new MyClassTakingLogger(logger)
myVar.doSth()
there was no(logger).error(any[Exception], "my err msg")
}
}
}
When running this, I get the following error:
[error] org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
[error] Invalid use of argument matchers!
[error] 2 matchers expected, 1 recorded:
[error] -> at org.specs2.mock.mockito.MockitoMatchers$class.any(MockitoMatchers.scala:47)
[error]
[error] This exception may occur if matchers are combined with raw values:
[error] //incorrect:
[error] someMethod(anyObject(), "raw String");
[error] When using matchers, all arguments have to be provided by matchers.
[error] For example:
[error] //correct:
[error] someMethod(anyObject(), eq("String by matcher"));
Which would make a lot of sense, but neither eq("my err msg") nor equals("my err msg") does the job as I get an error. What am I missing?

When you are using matchers to match parameters you have to use them for all parameters. as the all arguments have to be provided by matchers indicates.
Moreover if you use a specs2 matcher it needs to be strongly-typed. equals is a Matcher[Any] but there is no conversion from Matcher[Any] to a String which is what method accepts.
So you need a Matcher[T] or a Matcher[String] in your case. If you just want to test for equality, the strongly-typed matcher is ===
there was no(logger).error(any[Exception], ===("hey"))

I would like to add that you should be wary of default arguments, i.e. if using matchers when stubbing methods, make sure to pass argument matchers for all arguments, because default arguments will almost certainly have constant values - causing this same error to appear.
E.g. to stub the method
def myMethod(arg1: String, arg2: String arg3: String = "default"): String
you cannot simply do
def myMethod(anyString, anyString) returns "some value"
but you also need to pass an argument matcher for the default value, like so:
def myMethod(anyString, anyString, anyString) returns "some value"
Just lost half an hour figuring this out :)

Related

How to bind a class that extends a Trait with a monadic type parameter using Scala Guice?

I need to bind the implementation of this trait:
trait ClientRepository[F[_]] {
def list(): F[Iterable[ClientDTO]]
}
to this implementation:
import cats.effect.IO
#Singleton
class ClientRepositoryImpl #Inject()(db: OldDataBase, c: IOContextShift)
extends ClientRepository[IO] {
override def list(): IO[Iterable[ClientDTO]] = ???
}
I'm using Scala Play! v2.7.2 and Scala v2.12.8, with scala-guice v4.2.1. In order to bind the trait to its implementation I would like to do something like that in my Module.scala:
class Module(environment: Environment, configuration: Configuration)
extends AbstractModule
with ScalaModule {
override def configure() = {
bind[ClientRepository].to[ClientRepositoryImpl[IO]].in[Singleton]
}
}
And the error I get is:
[error] app/Module.scala:37:9: kinds of the type arguments (ClientRepository) do not conform to the expected kinds of the type parameters (type T).
[error] ClientRepository's type parameters do not match type T's expected parameters:
[error] trait ClientRepository has one type parameter, but type T has none
[error] bind[ClientRepository].to[ClientRepositoryImpl[IO]].in[Singleton]
[error] ^
[error] app/Module.scala:37:31: ClientRepositoryImpl does not take type parameters
[error] bind[ClientRepository].to[ClientRepositoryImpl[IO]].in[Singleton]
[error] ^
[error]
I've also tried:
bind[ClientRepository[IO]].to[ClientRepositoryImpl].in[Singleton]
Module.scala:37:9: kinds of the type arguments (cats.effect.IO) do not conform to the expected kinds of the type parameters (type T).
[error] cats.effect.IO's type parameters do not match type T's expected parameters:
[error] class IO has one type parameter, but type T has none
[error] bind[ClientRepository[IO]].to[ClientRepositoryImpl].in[Singleton]
[error] ^
and bind[ClientRepository[IO[_]]].to[ClientRepositoryImpl].in[Singleton]
Module.scala:37:27: cats.effect.IO[_] takes no type parameters, expected: one
[error] bind[ClientRepository[IO[_]]].to[ClientRepositoryImpl].in[Singleton]
[error] ^
What's the correct way to fix this?
I found the proper solution using Guice's TypeLiteral, after reading this SO answer and this one.
The working solution is:
// In Module.scala configure()
bind(new TypeLiteral[ClientRepository[IO]] {}).to(classOf[ClientRepositoryImpl])
because we must provide a class that can be instantiated (with a type parameter, that in our case is IO). TypeLiteral, which is a special class that enables you to specify a full parameterized type, can be used to create the actual binding to a particular implementation of our Repo[F[_]]. A class with a generic parameter cannot be instantiated but we can force Guice to pick up a specific ClientRepository that has been constructed with the type parameter cats.effect.IO.
Last but not least whenever you have to inject the trait ClientRepository you have to specify the type parameter as well. For instance:
class ClientResourceHandler #Inject()(
routerProvider: Provider[ClientRouter],
clientRepository: ClientRepository[IO]
)
the ClientResourceHandler needs to call the repo, so we're injecting it using the trait ClientRepository[IO] (not just ClientRepository).

Mockito stubbing method with value class argument fails with NullPointerException

Using typed value classes as IDs is a common pattern in Scala. However, it seems Mockito has an issue when stubbing methods that take value classes as arguments. In the example below, the first stub, with an actual value works just fine, but the second one, that uses an argument matcher throws NullPointerException.
The only reference to this I've found is this question but the solution shown there does not work. Anyone knows a solution to this, or a work-around?
Versions are: org.mockito:mockito-all:1.10.19 and org.specs2:specs2_2.11:2.4.15
import org.specs2.mutable.Specification
import org.specs2.matcher.Matchers
import org.specs2.mock.Mockito
case class ID[T](val id:Long) extends AnyVal
trait DAO[T]{
def get(id:ID[T]):T
}
class MockitoIDStubTest extends Specification with Mockito with Matchers{
"Mockito" should{
"properly stub with argument value" in {
val m = mock[DAO[String]
m.get(ID[String](1)).returns("abc")
m.get(ID[String](1)) must_== "abc"
}
"properly stub with argument matcher" in {
val m = mock[DAO[String]
m.get(any[ID[String]]).returns("abc")
m.get(ID[String](1)) must_== "abc"
}
}
}
[info] Mockito should
[info] + properly stub with argument value
[info] ! properly stub with argument matcher
[error] NullPointerException:(MockitoIDStubTest.scala:20)
[error] MockitoIDStubTest$$anonfun$1$$anonfun$apply$5$$anonfun$apply$6.apply( MockitoIDStubTest.scala:20)
It seems to work with scalamock and scalatest. I would still like to find a solution for Mockito tho, so I don't have to change a few hundred tests.
import org.scalatest._
import org.scalamock.scalatest.MockFactory
case class ID[T](val id:Long) extends AnyVal
trait DAO[T]{
def get(id:ID[T]):T
}
class ScalaMockIDStubTest extends WordSpec with MockFactory{
import language.postfixOps
"ScalaMock" should{
"properly stub with argument value" in {
val m = stub[DAO[String]
(m.get _) when(ID[String](1)) returns("abc")
assert( m.get(ID[String](1)) == "abc")
}
"properly stub with argument matcher" in {
val m = stub[DAO[String]
(m.get _) when(*) returns("abc")
assert( m.get(ID[String](1)) == "abc")
}
}
}

Scala Spec2 Mockito: Argument matchers with complex types

I'm trying to write a mock for a web service with Mockito. The mock should simulate a POST request using the play WS library.
/**
* Mock for the Web Service
*/
case class WSMock() extends Mockito {
val wsRequestHolder: play.api.libs.ws.WS.WSRequestHolder = mock[play.api.libs.ws.WS.WSRequestHolder]
val wsResponse: play.api.libs.ws.Response = mock[play.api.libs.ws.Response]
wsResponse.status returns 200
wsResponse.body returns "BODY RESP FROM WS"
val futureResponse = scala.concurrent.Future { wsResponse }
wsRequestHolder.post(any[Map[String,Seq[String]]]) returns futureResponse
}
When running the test I get the following error:
[error] InvalidUseOfMatchersException:
[error] Invalid use of argument matchers!
[error] 3 matchers expected, 1 recorded:
[error] -> at org.specs2.mock.mockito.MockitoMatchers$class.any(MockitoMatchers.scala:24)
[error]
[error] This exception may occur if matchers are combined with raw values:
[error] //incorrect:
[error] someMethod(anyObject(), "raw String");
[error] When using matchers, all arguments have to be provided by matchers.
[error] For example:
[error] //correct:
[error] someMethod(anyObject(), eq("String by matcher"));
[error]
[error] For more info see javadoc for Matchers class.
It looks to me as the any[...] expression using a complex type (with nested type parameters) does not correctly get resolved into a matcher. However, I don't see where the raw type comes into play.
What is the proper way to specify such a matcher for a parameter Map[String,Seq[String]]?
Thanks a lot!
wsRequestHolder.post(any[Map[String,Seq[String]]]) returns futureResponse
Note that post actually has a couple extra implicit parameters there:
def post [T] (body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]):
Promise[Response]
...which probably need to be matched explicitly, as in this spec2-users thread.
It seems the wsRequestHolder.post method requires three parameters, so Mockito expects you to send three (e.g. any[]) matchers, but you provided matchers for only one of them.

How do I leveraging SLF4J varargs logging in Play2.1 framework?

SLF4J's varargs on the logging calls are quite useful in my Java work
Logger log = LoggerFactory.getLogger( getClass() );
log.debug( "Hello, {}. The current time is {}", "robert", new Date() );
Attempting to do this simple example in Play 2.1 Framework/Scala and I run into the compiler rejecting me.
import play.api._
import play.api.mvc._
import org.slf4j.LoggerFactory
object Application extends Controller {
val log: org.slf4j.Logger = LoggerFactory.getLogger(getClass())
def hb = Action {
val message = makeMessage()
// COMPILER HATES THIS: ambiguous reference compiler error here
log.info("Hello {}. The current time is {}", "robert", new java.util.Date() )
Ok(message)
}
def makeMessage(): String = { return "stuff" }
}
[dm2-server] $ compile
[info] Compiling 2 Scala sources to /Users/bobk/work/dm2-server/target/scala-2.10/classes...
[error] /Users/bobk/work/dm2-server/app/controllers/Application.scala:16: ambiguous reference to overloaded definition,
[error] both method info in trait Logger of type (x$1: String, x$2: <repeated...>[Object])Unit
[error] and method info in trait Logger of type (x$1: String, x$2: Any, x$3: Any)Unit
[error] match argument types (String,String,java.util.Date)
[error] log.info("Hello {}. The current time is {}", "robert", new java.util.Date() )
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 1 s, completed Jun 6, 2013 10:54:41 AM
What is that error and how do I overcome it to call through to the SLF4J API? If I can't do that, how can I use the Play 2.1 Logging Framework to get varargs on my logging calls? Something is not right in Scala-land.
What version of SLF4J are you using? If you can go back to 1.6.6 or later, you can avoid this issue in ambiguity. Those two signatures unfortunately look the exact same to scala and the compiler can't seem to differentiate which one you mean. The common suggestion is to roll back to a version of SLF4J (if even possible for you) where this overloaded method ambiguity will not exist. More info can be found at the links below:
https://groups.google.com/forum/?fromgroups#!topic/scala-language/ms4IVIu-xGw
https://github.com/typesafehub/scalalogging/issues/16
The "quick fix" for this is as follows:
Just force the last argument to be type Any and that resolves the compiler's issue(s) (and makes for slightly less code...)
logger.debug("hello {} / {} ", "Hello", "World":Any)
Or in your case:
log.info("Hello {}. The current time is {}", "robert", new java.util.Date():Any)

How do I refer to enclosing "this" in a Scala macro?

The following macro, extracted from a larger example, is supposed to create a tree with nothing but a reference to this:
def echoThisImpl(c:Context): c.Expr[Any] = {
import c.universe._
val selfTree = This(c.enclosingClass.symbol)
c.Expr[AnyRef](selfTree)
}
def echoThis: Any = macro CallMacro.echoThisImpl
But a call to echoThis such as
object Testing extends App {
val thisValue = CallMacro.echoThis
println(thisValue)
}
fails to compile, with the message
[error] /home/rafael/dev/scala/goose/goose-macros/src/test/scala/Testing.scala:8: type mismatch;
[error] found : <noprefix>
[error] required: Any
[error] val thisValue = CallMacro.echoThis
If I set the -Ymacro-debug-lite flag the generated tree is This(newTermName("<local Testing>")).
There are two options of achieving what you want:
1) Use This(tpnme.EMPTY). Currently this doesn't compile, so you'll have to use This(newTypeName("")) instead, but in RC1 this will be fixed.
2) Use This(c.enclosingClass.symbol.asModule.moduleClass). Currently this doesn't work, because of https://issues.scala-lang.org/browse/SI-6394, but in RC1 this will be fixed.