Spock: can an interaction defined in setup() be replaced in a test case?

10,936

That's a tricky one. As stated in the docs, interactions declared in a then-block have precedence over interactions declared earlier. However, interactions declared in a then-block are scoped to the previous when-block. (This allows to have multiple when-then pairs.) Hence your last try doesn't work, but the following will:

def setup() {
    bar.message >> "hello"
}

def "say goodbye"() {
    when:
    def msg = foo.message

    then:
    bar.message >> "goodbye"
    msg == "goodbye"
}

I agree that it would be good for interactions declared in test methods to always override interactions declared in setup methods. Anyway, a good alternative to overriding interactions is for each test method to call a helper method that sets up the expected interactions for that test method.

Share:
10,936
John Q Citizen
Author by

John Q Citizen

Updated on June 04, 2022

Comments

  • John Q Citizen
    John Q Citizen almost 2 years

    I'm struggling to understand something about Spock interactions in a Groovy unit test.

    I have the following types:

    public interface Bar {
      public String getMessage();
    }
    
    public class Foo {
      private Bar bar;
      public void setBar(Bar bar) {
        this.bar = bar;
      }
      public String getMessage() {
        return bar.getMessage();
      }
    }
    

    and I then wrote the following Groovy/Spock test:

    class FooSpec extends Specification {
    
      private Bar bar;
      private Foo foo;
    
      def setup() {
        bar = Mock(Bar) { getMessage() >> "hello" }
        foo = new Foo()
        foo.bar = bar
      }
    
      def "say hello"() {
        expect:
        foo.message.equals("hello")
      }
    
      def "say goodbye"() {
        setup:
        bar.getMessage() >> "goodbye"
    
        expect:
        foo.message.equals("goodbye")
      }
    }
    

    The code creates a mock Bar instance in the setup, initializes Bar.getMessage() to return hello, and assigns this to a new Foo instance.

    The first test verifies that foo.getMessage() is equal to hello.

    The second test tries to modify the bar mock so that it's getMessage method returns goodbye. We then expect that foo.getMessage() (which delegates to bar.getMessage()) would then return goodbye. However the test fails as follows:

    FooSpec:say goodbye:26 Condition not satisfied

    because foo.message is still equal to hello.

    I also tried the following:

    def "say goodbye"() {
      when:
      bar.getMessage() >> "goodbye"
    
      then:
      foo.message.equals("goodbye")
    }
    

    and:

    def "say goodbye"() {
      when:
      no_op()
    
      then:
      bar.getMessage() >> "goodbye"
      foo.message.equals("goodbye")
    }
    

    But both failed with the same hello does not equal goodbye message.

    I'm probably still thinking in Mockito mode, and assume that an interaction is the equivalent of a when(...).thenReturn(...) expression, and that later interactions would override earlier interactions.

    Is there a simple way using Spock to declare an interaction in a setup method, then override that interaction in a test case? Or do I need to remove the setup() method and basically add a setup: block to each test case?

  • John Q Citizen
    John Q Citizen about 10 years
    Thanks for the comment. I'm going to eschew using a setup() method and change my test methods to initialize their state directly (this is also motivated by the fact that I often use a where: block in my test methods, and found to my surprise that the setup() method was called after the where: block). I think I can understand your example above, but it's not intuitive (it still looks like setup code in the then: block to me).
  • falsarella
    falsarella almost 8 years
    @PeterNiederwieser, what Spock version have you used in this example? We've tried this and a lot of other variations with Spock 1.0, but it didn't work.
  • Zorobay
    Zorobay over 2 years
    @JohnQCitizen I often use this tactic when I want to both assert that a service method has been called a certain number of times, as well as controlling what data is returned. I think in this example, I would put it in a given: block.