Implicit conversions in Specs2 gone mad

Hidiho!

In this resurrection post I want to talk a little bit about a problem we faced recently at CarJump (how I ended up there is an story for another post) with Specs2 and Mockito. Actually, the issue I want to address is subtle and appeared in a very specific scenario. Lets start describing such scenario.

Disclaimer: the scenario below is valid for specs2 version 3.7.x – with specs2 2.x everything was fine. Implicit conversions defined by specs2 changed quite a bit between those two versions.

First, we have a specs2 test specification. Something as simple as the following:

class ImplicitsSpec extends Specification {
  "my spec" should {
    "do something" in {
      pending
    }
  }
}

This works just fine, it is just a simple specs2 Specification. Next comes adding mockito. In specs2, it is nothing more then adding the Mockito trait to the test suite. Or, and this is where it gets you, wherever you define your mocks. This is the pattern we started to used recently:

object ImplicitsSpec extends Mockito {
  // my common test vals here
}

Putting that code into words, we create a companion object that will hold all common vals used in the tests. We actually are starting to do this kind of thing in lots of places, and this is the first time it was a problem.

So, how are we to use those common values? Just import the companion object members. Applying this strategy to the spec presented earlier, the result would be something like:

class ImplicitsSpec extends Specification {
  import ImplicitsSpec._

  "my spec" should {
    "do something" in {
      pending
    }
  }
}

Pretty simple and nothing can go wrong there, right? Well… wrong. If you try to compile the code above, you will get an error like the following:

/src/test/scala/com/jcranky/specs2/implicits/ImplicitsSpec.scala:11: type mismatch;
[error]  found   : org.specs2.specification.core.Fragment
[error]  required: org.specs2.matcher.Matcher[String]
[error]     "do something" in {

Wait, what?

What happens is that the specs2 Mockito trait brings into context several other implicit conversions, not only mock related stuff. And what’s more, imported conversions have higher precedence over conversions got from class definitions. In our case, it means that whatever comes from

import ImplicitsSpec._

comes before what we get from extending Specification. In this case, in practice, we are losing a conversion from Fragment to Matcher. At this moment, I couldn’t find exactly where this conversion is, but there is a few workarounds to fix this. The first one is to change the companion object declaration to be:

object ImplicitsSpec extends Specification with Mockito

This will bring all relevant implicit conversions to the same scope. Another common solution would be to declare the test specification like below, and remote Mockito from the companion object:

class ImplicitsSpec extends Specification with Mockito

The problem with this solution is that then you cannot create common mock objects in the companion object. There are obviously other solutions, but they usually get more complicated. Still, if you know the root of the problem, please leave a comment!

Advertisement
This entry was posted in scala and tagged , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s