Search code examples
spockmaven-surefire-plugin

Spock/Groovy - Condition evaluation order


I have some tests like:

when:
testedFunction()

then:
noExceptionThrown()
1 * service.action()

When I run the test, it fails, because some code in testedFunction throws an exception. In the test results (via maven-surefire-plugin), the failure is that service.action() was never called. But I don't get any test failure about the exception being thrown.

If I remove the 1 * service.action() line, then I get a test failure about the exception being thrown, and I can easily debug what the actual issue is

Is there some logic to which conditions it chooses to evaluate first/report? I would have expected either that it should report the first condition that fails, or report all of them. I can't find any setting to report all vs one either, but I'm not super familiar with spock.

Minimal example, not sure if there's a place to put this since meetspock.appspot.com is broken

import spock.lang.*

class MyFirstSpec extends Specification {
  class Example {
    public function1() {
       throw new Exception("");
    }
  }
  interface Service {
    void func();
  }
  def "should give an exception failure"() {
    given:
    def service = Mock(Service)
    def ex = new Example()

    when:
    ex.function1()

    then:
    noExceptionThrown()
    1 * service.func()
  }
}
​

Solution

  • You are running into an edge case of Spock.

    Interaction conditions, such as 1 * service.func() are always evaluated first regardless of the order of other conditions. As you also used noExceptionThrown(), the exception thrown during the execution actually catched and later asserted, but that doesn't happen as interactions have priority. If you'd removed the noExceptionThrown() then you'd actually see it. Therefor, it I'd advise against using noExceptionThrown() unless it is the only statement in the then block, as it is implied in every other case.

    If you want the noExceptionThrown() to be evaluate first, you need to use invocation ordering as mentioned in the docs, i.e, putting the interaction in a second then block.

    import spock.lang.*
    
    class MyFirstSpec extends Specification {
      class Example {
        public function1() {
           throw new Exception("");
        }
      }
      interface Service {
        void func();
      }
      def "should give an exception failure"() {
        given:
        def service = Mock(Service)
        def ex = new Example()
    
        when:
        ex.function1()
    
        then:
        noExceptionThrown()
          
        then:
        1 * service.func()
      }
    }
    

    Groovy Web Console