8
votes

I'm trying out unit testing with Play framework in scala. I've written a class that checks whether the config is correct (I have a bit more error handling but I actually use this code for my test now):

class TaskQueueConfig(conf: Configuration) {
  val schedulingEnabled = conf.getBoolean("schedulingEnabled").get
  val processingEnabled = conf.getBoolean("processingEnabled").get
  val queueName = conf.getString("queue").get
}

I'm testing this using play 2.1.1's default test setup:

class ConfigTestSpec extends Specification with Mockito with CalledMatchers {
  "TaskQueueConfig" should {
     "verify calls" in {
      val tqConf = mock[Configuration]
      tqConf.getString("queue") returns Some("queueName")
      tqConf.getBoolean("schedulingEnabled") returns Some(true)
      tqConf.getBoolean("processingEnabled") returns Some(true)
      Logger.error("setup done")

      val config = new TaskQueueConfig(tqConf)

      there was one(tqConf).getString("queue")
      there were two(tqConf).getBoolean(any[String])
      there were one(tqConf).getBoolean("schedulingEnabled")
      there were one(tqConf).getBoolean("processingEnabled")
    }
  }
}

I'm getting the following error:

[error] x verify calls
[error]    The mock was not called as expected:
[error]    configuration.getString$default$2();
[error]    Wanted 1 time:
[error]    -> at config.ConfigTestSpec$$anonfun$2$$anonfun$apply$4$$anonfun$apply$12.apply(ConfigTestSpec.scala:61)
[error]    But was 2 times. Undesired invocation:
[error]    -> at config.TaskQueueConfig.<init>(TaskQueueConfig.scala:10) (ConfigTestSpec.scala:61)

This is very strange, because the code is very isolated and there is clearly just 1 conf.getString call in TaskQueueConfig. Line 10 is the line with the getString method on it. Line 61 is the line with "there was one(tQConf).getString"

How can I fix this issue?

(there is no difference between were and was).

PS: I know this example is pretty useless to test, but I have more complex configs where there are some rules that need to be tested.

UPDATE 1 The getString method has two parameters, the second parameter has a default value of None (it's type is Option[Set[String]]). When I explicitly add the None's to the setup and verification, it still doesn't work. But when I add null instead, I get it working.

  val tqConf = mock[Configuration]

  tqConf.getString("queue", null) returns Some("queueName")
  tqConf.getBoolean("schedulingEnabled") returns Some(true)
  tqConf.getBoolean("processingEnabled") returns Some(true)

  val c = new TaskQueueConfig(tqConf)

  there was one(tqConf).getString("queue", null)
  there was one(tqConf).getString(any[String], any[Option[Set[String]]])
  there were two(tqConf).getBoolean(any[String])
  there was one(tqConf).getBoolean("schedulingEnabled")
  there was one(tqConf).getBoolean("processingEnabled")

  c.processingEnabled must beTrue
  c.schedulingEnabled must beTrue
  c.queueName must be("queueName")

So I guess the question now is, why do I have to use null?

1
Why are you verifying what was called on the mock configs vs just verifying the end state of the created object? If you stubbed correctly, you should just be verifying that the property values of the TaskQueueConfig match the stubbed values you set up. I know this does not answer your question, but it certainly would eliminate this issue for you...cmbaxter
In this particular case, of course, but I'm coming from a larger example where I had this, and I narrowed it down to the smallest possible example, so please bear with me!Jaap
My two initial thoughts were that either the stubbing itself was being counted (therefore making it two calls) or you had a scoping issue and another tests sequence was also being counted thus the additional call to getString. I tested the first theory myself and it does not look to me like mockito is accidentally including the stubbing in the call counts. For the scoping issue, if your real code is as it is here and the mock is not created globally, then that should also not be an issue.cmbaxter
The code is exactly as shown, without imports only. It wasn't the only one in the file but I commented out the rest and replaced them with todo...Jaap
Also stubbing is not counted, the verifications for getBoolean succeed when I remove the get string verification.Jaap

1 Answers

4
votes

You have to use null in the two-argument getString() call because Mockito requires you to either use use matchers for all arguments or none at all.

So mixing "queue" (a literal) with any[Option[Set[String]]] (a matcher) won't work. Sometimes Mockito can work out that you've done that and it gives you an error, but it seems like you're unlucky here...

Try using matchers in all positions like this:

tqConf.getString(org.mockito.Matchers.eq("queue"), any[Option[Set[String]]]) returns Some("queueName")

And verifying:

there was one(tqConf).getString(eq("queue"), any[Option[Set[String]]])