Problems with mocks and matchers

690 views
Skip to first unread message

Steffen Fritzsche

unread,
Aug 10, 2011, 6:51:00 AM8/10/11
to specs2...@googlegroups.com
Hi,

I've got a problem using the specs2 matchers together with mocks. It works fine while specifying return values, e.g.

    db.createProject(be_==(p0)) returns p1

runs without any problems.

However, when using specs2 matchers for call verification it does only accept matchers from "org.mockito.Matchers" or "org.specs2.mock.mocking.MockitoMatchers". For example this runs fine

    there was one(ctx).complete(org.mockito.Matchers.eq(p1))(any)

but this 

    there was one(ctx).complete(be_==(p1))(any)

doesn't work. It's throwing an exception that mockito expects that either non or all parameters are wrapped with a matcher. Although, this isn't a big deal it would be very nice if one was able to use the specs2 matchers also for call verification. Or am I doing something wrong?

Cheers,
Steffen

etorreborre

unread,
Aug 10, 2011, 7:23:09 PM8/10/11
to specs2...@googlegroups.com
Hi Steffen,

There must be something else at play here. When I try the following sample spec, it works fine:

package examples

import org.specs2._
import matcher._
import mock._

class TestSpec extends Specification with Mockito { def is =

  "test"    ^
  "e1" ! e1 ^
            end

  def e1 = {
    val db = mock[DB]
    db.complete(1)("")
    there was one(db).complete(be_==(1))(any)
  }
  trait DB {
    def complete(a: Int)(b: Any): Any
  }
}
 
Can you "trim down" your specification to a simple one showing the issue?

Eric.

Steffen Fritzsche

unread,
Aug 11, 2011, 4:43:17 AM8/11/11
to specs2...@googlegroups.com

Hi Eric,

since your example runs fine, I guess the problem is specific to the mocked object. I try to mock spays RequestContext (see http://spray.cc). So the stripped down failing sample looks as follows


import org.specs2.Specification

import org.specs2.mock.Mockito

import cc.spray.RequestContext

import cc.spray.marshalling.DefaultMarshallers


class MatcherSpec extends Specification { def is =    

    "This specification should"                                     ^

        "be able to use specs2 matchers within verification code"   ! c().testIt^

                                                                    end

    case class c() extends Mockito with DefaultMarshallers {

        def testIt = { 

            val ctx = mock[RequestContext]

            ctx.complete("this is a test")

            there was one(ctx).complete(be_==("this is a test"))(any)

        }

    }

}


Nevertheless, if you replace the specs2 matcher with a mockito matcher this runs fine.

   there was one(ctx).complete(org.mockito.Matchers.eq("this is a test"))(any)

So I guess, since spray brings in an in-scope marshaller this might confuse something. If you are interested you can have a look at the RequestContext's code on github, see

https://github.com/spray/spray/blob/master/spray-server/src/main/scala/cc/spray/RequestContext.scala for details.

For me it now looks like a very specific corner case, I don't know if there are other examples that fail because of a situation like that.


Steffen

etorreborre

unread,
Aug 11, 2011, 8:03:02 AM8/11/11
to specs2...@googlegroups.com
Hi Steffen,

I think I get it. The context method expects a parameter of type A.

If you use a Mockito matcher, like 'eq', it works fine because that function returns an object of type A. In the usual case where you use specs2 matchers, as in my example, there is an implicit conversion happening. My example is actually equivalent to:

    there was one(db).complete(argThat(be_==(1)))(any)

where argThat is defined as:

   implicit def argThat[T, U <: T](m: org.specs2.matcher.Matcher[U]): T = 
      org.mockito.Matchers.argThat(new org.specs2.mock.HamcrestMatcherAdapter(m))

It "adapts" a specs2 matchers to a hamcrest one and calls the argThat method from Mockito, returning the expected "T" value.

This all works fine unless in the case where the implicit is not called, which I think is what happens here. In your code, Matcher[T] is considered to be the expected type A for the "complete" method and is taken as a raw value. I don't fully explain myself how the type constraint ": Marshaller" is defined but I guess that this is provided by the DefaultMarshallers trait.

In conclusion, to make your example work you need to call argThat explicitly.

Eric.
Reply all
Reply to author
Forward
0 new messages