Spock: can an interaction defined in setup() be replaced in a test case?
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.
John Q Citizen
Updated on June 04, 2022Comments
-
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, initializesBar.getMessage()
to returnhello
, and assigns this to a newFoo
instance.The first test verifies that
foo.getMessage()
is equal tohello
.The second test tries to modify the
bar
mock so that it'sgetMessage
method returnsgoodbye
. We then expect thatfoo.getMessage()
(which delegates tobar.getMessage()
) would then returngoodbye
. However the test fails as follows:FooSpec:say goodbye:26 Condition not satisfied
because
foo.message
is still equal tohello
.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 thesetup()
method and basically add asetup:
block to each test case? -
John Q Citizen about 10 yearsThanks 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 awhere:
block in my test methods, and found to my surprise that thesetup()
method was called after thewhere:
block). I think I can understand your example above, but it's not intuitive (it still looks like setup code in thethen:
block to me). -
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 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.