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")
}
}
}
Related
I would expect this test to pass:
import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}
class FruitImpl {
case class FruitName(name: String)
def getFruitName: Option[FruitName] = {
Some(FruitName("apple"))
}
}
class FruitSpec extends FlatSpec with Matchers with MockFactory {
val f = mock[FruitImpl]
(f.getFruitName _).expects().returning(None)
behavior of "getFruitName method"
it should "return None" in {
f.getFruitName should === (None)
}
}
But it fails with:
[error] my/path/QuestionTest.scala:13: overriding method getFruitName in class FruitImpl of type => Option[this.FruitName];
[error] method getFruitName has incompatible type
[error] val f = mock[FruitImpl]
[error] ^
This works, though:
import org.scalamock.scalatest.MockFactory
import org.scalatest.{FlatSpec, Matchers}
case class FruitName(name: String)
class FruitImpl {
def getFruitName: Option[FruitName] = {
Some(FruitName("apple"))
}
}
class FruitSpec extends FlatSpec with Matchers with MockFactory {
val f = mock[FruitImpl]
(f.getFruitName _).expects().returning(None)
behavior of "getFruitName method"
it should "return None" in {
f.getFruitName should === (None)
}
}
The only difference is that the case class FruitName is defined outside of the class FruitImpl. Why does one version of the code fails and the other doesn't? What should one do to fix the error in the first example?
Without looking at the ScalaMock code, I'd say that the mock is not a true derivation of FruitImpl in the OO sense. Its purpose is to allow method interception, so it only deals with the facade. It follows then that the mock actually has no definition of the path dependent type FruitName, and so cannot work with a method signature that depends on it.
This is precisely why it does work when the FruitName definition is moved out of FruitImpl. It now exists independently of the mock, who's method signatures depending on it then work as expected.
Suppose I had this interface and class:
abstract class SomeInterface{
def doSomething : Unit
}
class ClassBeingTested(interface : SomeInterface){
def doSomethingWithInterface : Unit = {
Unit
}
}
Note that the doSomethingWithInterface method does not actually do anything with the interface.
I create a test for it like this:
import org.specs2.mutable._
import org.specs2.mock._
import org.mockito.Matchers
import org.specs2.specification.Scope
trait TestEnvironment extends Scope with Mockito{
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class ClassBeingTestedSpec extends Specification{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in new TestEnvironment {
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
This test passes. Why? Am I setting it up wrong?
When I get rid of the scope:
class ClassBeingTestedSpec extends Specification with Mockito{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
The test fails as expected:
[info] x calls the doSomething method of the given interface
[error] The mock was not called as expected:
[error] Wanted but not invoked:
[error] someInterface.doSomething();
What is the difference between these two tests? Why does the first one pass when it should fail? Is this not an intended use of Scopes?
When you mix-in the Mockito trait to another trait you can create expectations like there was one(interface).doSomething. If such an expression fails it only returns a Result, it doesn't throw an Exception. It then gets lost in a Scope because it is just a "pure" value inside the body of a trait.
However if you mix-in the Mockito trait to a mutable.Specification then an exception will be thrown on a failure. This is because the mutable.Specification class specifies that there should be ThrownExpectations by mixing in that trait.
So if you want to create a trait extending both Scope you can either:
create the trait from inside the specification and not have it extend Mockito:
class MySpec extends mutable.Specification with Mockito {
trait TestEnvironment extends Scope {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
...
}
create trait and specification as you do, but mix-in org.specs2.execute.ThrownExpectations
trait TestEnvironment extends Scope with Mockito with ThrownExpectations {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class MySpec extends mutable.Specification with Mockito {
...
}
I'm using specs2 (v1.8.2) on with Scala (v2.9.1) to write acceptance tests. Following the example at http://etorreborre.github.com/specs2/guide/org.specs2.guide.SpecStructure.html#Contexts, I have the following Specification and context case class:
import org.specs2._
class testspec extends SpecificationWithJUnit { def is =
"test should" ^
"run a test in a context" ! context().e1
}
case class context() {
def e1 = 1 must beEqualTo(1)
}
I get a compiler error:
error: value must is not a member of Int def e1 = 1 must beEqualTo(1)
when compiling the context case class.
Obviously I'm new to specs2 (and to Scala). References to the appropriate documentation would be greatly appreciated.
Obviously the doc is wrong (was wrong, I fixed it now).
The correct way to write declare the case class for a context is usually to include it in the Specification scope:
import org.specs2._
class ContextSpec extends Specification { def is =
"this is the first example" ! context().e1
case class context() {
def e1 = List(1,2,3) must have size(3)
}
}
Otherwise if you want to reuse the context in another specification, you can, as Dario wrote, access the MustMatchers functionalities either by importing the MustMatchers object methods or by inheriting the MustMatchers trait.
must is not a member of Int, because to "must" is not known in the context of your "context" class. Put the method "e1" inside your specification class and it should work. E.g.
import org.specs2._
class TestSpec extends Specification { def is =
"test should" ^
"run a test in a context" ! e1 ^
end
def e1 = 1 must beEqualTo(1)
}
edit
Ah, I see what you want ;-). This should work like this:
To have the matchers in the scope of the context class you have to import the MustMatchers.
import org.specs2._
import matcher.MustMatchers._
class ContextSpec extends Specification { def is =
"this is the first example" ! context().e1
}
case class context() {
def e1 = List(1,2,3) must have size(3)
}
I am using mockito and trying to mock a scala object.
object Sample { }
//test
class SomeTest extends Specification with ScalaTest with Mockito {
"mocking should succeed" in {
val mockedSample = mock[Sample]
}
}
This gives me two compilation errors.
error: Not found type Sample
error: could not find implicit value for parameter m:
scala.reflect.ClassManifest[<error>]
If I change Sample from object to class it works.
Is is possible to mock scala objects with mockito? If yes how?
As written, your Sample is a pure singleton. Its type is its own and there is only one member of that type, period. Scala objects can extend another class (possibly abstract, if it supplies the necessary definitions to make it a concrete) and traits. Doing that gives it a type identity that includes those ancestors.
I don't know what Mockito is really doing, but to my mind, what you're asking for is strictly at odds with what a Scala object is.
Keep in mind that you can mock the methods of an object if you lift them to functions.
case class Person(name: String)
object Person {
def listToJson(lp: List[Person]) = "some actual implementation"
}
class ClassUnderTest(listToJson: (List[Person]) => String = Person.listToJson(_)) {
def testIt(lp: List[Person]) = listToJson(lp)
}
import org.specs._
import org.specs.mock.Mockito
import org.mockito.Matchers._
class ASpec extends Specification with Mockito {
"a thing" should {
"do whatever" in {
val m = mock[(List[Person]) => String]
val subject = new ClassUnderTest(m)
m(Nil) returns "mocked!"
subject.testIt(Nil) must_== "mocked! (this will fail on purpose)"
}
}
}
Here I'm not mocking the object Person, but the method on it (which is probably what the OP was intending).
The test result shows the mocking works:
[info] == ASpec ==
[error] x a thing should
[error] x do whatever
[error] 'mocked![]' is not equal to 'mocked![ (this will fail on purpose)]' (ASpec.scala:21)
[info] == ASpec ==
Meanwhile, the production-time usage of the ClassUnderTest is simply new ClassUnderTest due to the injected function being a default argument.
Since version 1.16.0 of mockito-scala it is possible to mock Scala objects, you can check the docs here, but this is an example of how it would look like.
object FooObject {
def simpleMethod: String = "not mocked!"
}
"mock" should {
"stub an object method" in {
FooObject.simpleMethod shouldBe "not mocked!"
withObjectMocked[FooObject.type] {
FooObject.simpleMethod returns "mocked!"
//or
when(FooObject.simpleMethod) thenReturn "mocked!"
FooObject.simpleMethod shouldBe "mocked!"
}
FooObject.simpleMethod shouldBe "not mocked!"
}
}
I've recently released ScalaMock, a mocking library for Scala that can, among other things, mock singleton (and companion) objects.
I'm trying to mock a method call that takes a call-by-name argument:
import org.scalatest.WordSpec
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
trait Collaborator {
def doSomething(t: => Thing)
}
trait Thing
#RunWith(classOf[JUnitRunner])
class Test extends WordSpec with MockitoSugar {
"The subject under test" should {
"call the collaborator" in {
// setup
val m = mock[Collaborator]
val t = mock[Thing]
// test code: this would actually be invoked by the SUT
m.doSomething(t)
// verify the call
verify(m).doSomething(t)
}
}
}
I'm primarily interested in Mockito since that's what I'm using, but I'd be interested to see whether any of the major mock frameworks is capable of this kind of testing. The Test fails at runtime on the verify line, with an error like
Argument(s) are different! Wanted:
collaborator.doSomething(
($anonfun$apply$3) <function>
);
-> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:27)
Actual invocation has different arguments:
collaborator.doSomething(
($anonfun$apply$2) <function>
);
-> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:24)
If I'm understanding the situation correctly, the compiler is implicitly wrapping t in a nullary function that returns t. The mock framework is then comparing that function to the one produced in the test code, which is equivalent but not equals().
My case is a relatively simple version of the problem, but I think this would be an issue with any higher-order function.
This looks ugly, but hopefully it can help you to find good solution:
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
trait Collaborator {
def doSomething(t: => Thing)
}
trait Thing
new MockitoSugar {
// setup
val m = mock[Collaborator]
val t = mock[Thing]
m.doSomething(t)
classOf[Collaborator].getMethod("doSomething", classOf[Function0[_]]).invoke(
verify(m),
new Function0[Thing] {
def apply() = null
override def equals(o: Any): Boolean = t == o.asInstanceOf[Function0[Thing]].apply()
})
}
You can try specs2. In specs2, we "hijack" the Mockito Invocation class to account for byname parameters:
trait ByName { def call(i: =>Int) = i }
val byname = mock[ByName]
byname.call(10)
there was one(byname).call(10)
This problem seems to be specific to by-name invocations because in regular higher order functions you can match against the explicit FunctionX object:
verify(collaborator).somethingElse(any(Function2[String, Thing]))
in the by-name case the wrapping of the argument into a Function0 is done implicitly, and Alexey's answer shows how to invoke the mock with an explicit parameter.
You could write something akin to your own verify which would apply arguments captured by mockito.
Mockito internally records invocation and their arguments with e.g.:
http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java