Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 987
John Q Citizen Avatar asked Mar 06 '14 05:03

John Q Citizen


1 Answers

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.

like image 139
Peter Niederwieser Avatar answered Nov 11 '22 22:11

Peter Niederwieser