Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this specs2 test using Mockito passing?

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?

like image 324
mushroom Avatar asked Dec 06 '13 16:12

mushroom


1 Answers

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:

  1. 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)
      }
      ...
    }
    
  2. 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 {
      ...
    }
    
like image 138
Eric Avatar answered Nov 18 '22 05:11

Eric