Spock: interactions in given/setup blocks should have precedence over those in setup methods

Created on 30 Aug 2015  路  23Comments  路  Source: spockframework/spock

Originally reported on Google Code with ID 199

Describe the enhancement you have in mind.

interactions in given/setup blocks should have precedence over those in setup methods
as discussed in: http://groups.google.com/group/spockframework/browse_thread/thread/3e20f7466dd6248b

Is it possible to override the behaviour (return value) for one
specific test method: see example below (also available at:
http://meetspock.appspot.com/script/36001)
"testing exceptional behaviour" fails where I want it to succeed

interface MyInterface {
    int myMethod()

}

class MyInterfaceTest extends Specification {
    def myInterface = Mock(MyInterface)

    def setup() {
        myInterface.myMethod() >> 1
    }

    def "testing regular behaviour"() {
        when:
         def result = myInterface.myMethod()
        then:
         result == 1
     }

    def "testing exceptional behaviour"() {
        given:
         myInterface.myMethod() >> 2
        when:
         def result = myInterface.myMethod()
        then:
         result == 2
      } 
}

Which particular problem would this enhancement solve for you?
it would ease the setup of testing expetional behaviour, where for all other tests
use the same 'normal' behaviour of the mock, so you don't have to setup this for each
test.


Please provide any additional information below. You can also assign
labels.

Reported by frederic.pape on 2011-09-13 11:08:13

Module-Core Type-Enhancement

Most helpful comment

@vanta it might look like a hack to you, but it is not. What you describe is just adding another stub call after the first one. Changing that behavior would break the existing code for many others, and having a valid way to override it I don't see it as necessary to change it. Keep in mind that adding more ways to do the same thing will complicate the syntax and makes it harder for everyone, to understand what is happening.

People that knows spock will fix it quickly we have to try to bring more people into the fold, and those people are using Junit.

I am migrating every single project that I find from Junit into Spock, training everyone and this bug is very confusing for new people used to Junit

All 23 comments

It would make more sense to me to override the interactions defined in the setup method
within the setup/given block

Reported by Alessandro.Mecca on 2012-11-20 16:52:59

I completely agree with this change. Just like in junit any changes I make to mocks
or global variables are overridden in an individual test. I found it very confusing
when I would change the behavior in an individual tests setup block to find out it
is ignored. The specification setup should be to setup the base line for all tests.
Then each individual test should be able to change behavior as it sees fit.

Reported by Chef098 on 2013-07-05 14:35:29

+1 This change also makes easier to migrate from testng or junit to Spock, as it is
the default behaviour on these frameworks.

Reported by fsamir on 2015-07-16 05:35:45

+1

As a workaround while keeping the Behaviour Definition at the right place, I did currently sth like this:

class MyInterfaceTest extends Specification {
    def myInterface = Mock(MyInterface)
    def myMethodReturn

    def setup() {
        myMethodReturn = 1
        myInterface.myMethod() >> {return myMethodReturn}
    }

    def "testing regular behaviour"() {
        when:
         def result = myInterface.myMethod()
        then:
         result == 1
     }

    def "testing exceptional behaviour"() {
        given:
         myMethodReturn = 2
        when:
         def result = myInterface.myMethod()
        then:
         result == 2
      } 
}

+1

+1

I think the best way to do this while retaining a) backward compatibility and b) make it optional (sometime I don't want it to be overriden) would be a new syntax/method, something like:

def "overriding setup mocks"() {
    given:
    myMock.redefine {
        oldMethod >> "bla"
    }

    ....
}

That would work great!

+1

This issue was created in 2011, any update on it? It's still marked "New"...

+1 any news regarding this? I am selling finally to my new team Spock, and this is one of the things that is confusing for them coming from Junit

+1

If you are using a Mock/Spy (this doesn't work for Stub), you can override the stubbing behavior, if you define an interaction in the then block. See http://spockframework.org/spock/docs/1.2-RC1/all_in_one.html#_combining_mocking_and_stubbing

    def "testing exceptional behaviour"() {
        given:
         myInterface.myMethod() >> 1
        when:
         def result = myInterface.myMethod()
        then:
         result == 2
          _ * myInterface.myMethod() >> 2
      } 

Only use _ if you don't care about the number of calls.

Hi @leonard84

I am confused why this is closed as the behaviour is still the same.
Currently there is no way to write a test given myMethod returns 2 THEN behave special.
It is always given myMethod returns 2 THEN myMethod returns 2 and behave special
Which should not be the flow and makes the test be less readable.
So could you pleasse explain why this issue was closed even though the behaviour is still the same?

@huehnerlady it is by design, that the interactions with verifications in the then block have precedence over the given/setup blocks. I've updated the example above, it didn't really explain it, since both interactions returned 2.

See also this variant.

  def setup() {
         myInterface.myMethod() >> 1
  }

    def "testing exceptional behaviour"() {
        when:
         def result = myInterface.myMethod()
        then:
         result == 2
          _ * myInterface.myMethod() >> 2
      } 

@leonard84 I believe that the problem is why we cannot specify such precedence in given: block as this is the place for it, if you don't want to verify anything. _ * method() looks like a hack here.

@leonard84 imagine the scenario where you define a mock in the setup because you don't want to add it in every single test, but there are few cases where you need to override the expectation in the given of the test, that is what is missing.
Currently we have to remove it from the setup forcing us to declare it in all the givens for almost all the tests just because only few of them need it in a different way

Case 1)

    List myMock = Mock(List) {
        get(0) >> 1
    }

    def "normal testcase"() {
        when:
        def result = myMock.get(0)

        then:
        result == 1
    }

    def "exception testcase"() {
        when:
        def result = myMock.get(0)

        then:
        result == 2
        1 * myMock.get(0) >> 2
    }

works right now.
Case 2)

   List myMock = Mock(List) {
        1 * get(0) >> 1
    }

    def "normal testcase"() {
        when:
        def result = myMock.get(0)

        then:
        result == 1
    }

    def "exception testcase"() {
        when:
        def result = myMock.get(0)

        then:
        result == 2
        1 * myMock.get(0) >> 2
    }

will fail. I closed it because the ticket described case 1) which works correctly.

@leonard84 I believed that ticket describes 3rd case:

    List myMock = Mock(List) {
        get(0) >> 1
    }

    def "exception testcase"() {
        given:
        myMock.get(0) >> 2

        when:
        def result = myMock.get(0)

        then:
        result == 2
    }

and the question is why it cannot be done this way as putting this "overwrite" behaviour in then block looks like a hack.

@vanta it might look like a hack to you, but it is not. What you describe is just adding another stub call after the first one. Changing that behavior would break the existing code for many others, and having a valid way to override it I don't see it as necessary to change it. Keep in mind that adding more ways to do the same thing will complicate the syntax and makes it harder for everyone, to understand what is happening.

@vanta it might look like a hack to you, but it is not. What you describe is just adding another stub call after the first one. Changing that behavior would break the existing code for many others, and having a valid way to override it I don't see it as necessary to change it. Keep in mind that adding more ways to do the same thing will complicate the syntax and makes it harder for everyone, to understand what is happening.

People that knows spock will fix it quickly we have to try to bring more people into the fold, and those people are using Junit.

I am migrating every single project that I find from Junit into Spock, training everyone and this bug is very confusing for new people used to Junit

I created a new issue for that behaviour now:
https://github.com/spockframework/spock/issues/962

@vanta it might look like a hack to you, but it is not. What you describe is just adding another stub call after the first one. Changing that behavior would break the existing code for many others, and having a valid way to override it I don't see it as necessary to change it. Keep in mind that adding more ways to do the same thing will complicate the syntax and makes it harder for everyone, to understand what is happening.

But maybe it would be nice add a for instance some flag to Stub/Mock setup:

@Beta
  public <T> T Mock(
    @NamedParams({
      @NamedParam(value = "name", type = String.class),
      @NamedParam(value = "additionalInterfaces", type = List.class),
      @NamedParam(value = "defaultResponse", type = IDefaultResponse.class),
      @NamedParam(value = "verified", type = Boolean.class),
      @NamedParam(value = "useObjenesis", type = Boolean.class),
      @NamedParam(value = "deepOverride", type = Boolean.class) // allow to override in given section
    })
      Map<String, Object> options) {
    invalidMockCreation();
    return null;
  }

What do You think? @leonard84

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kriegaex picture kriegaex  路  5Comments

woldie picture woldie  路  8Comments

donbeave picture donbeave  路  5Comments

Derbeth picture Derbeth  路  10Comments

szpak picture szpak  路  8Comments