Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interactions in given/setup blocks should have precedence over those in setup methods #321

Closed
robfletcher opened this issue Aug 30, 2015 · 23 comments

Comments

@robfletcher
Copy link
Contributor

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

@robfletcher
Copy link
Contributor Author

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

@robfletcher
Copy link
Contributor Author

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

@robfletcher
Copy link
Contributor Author

+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

@alwinmark
Copy link

+1

@alwinmark
Copy link

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
      } 
}

@sf-git
Copy link

sf-git commented Oct 8, 2015

+1

1 similar comment
@mjwest10
Copy link

+1

@pauxus
Copy link

pauxus commented Jan 21, 2016

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"
    }

    ....
}

@mjwest10
Copy link

That would work great!

@vanta
Copy link

vanta commented May 10, 2016

+1

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

@EduardoSolanas
Copy link

+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

@j-gurda
Copy link

j-gurda commented Aug 3, 2018

+1

@leonard84
Copy link
Member

leonard84 commented Aug 31, 2018

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.

@huehnerlady
Copy link

huehnerlady commented Jan 14, 2019

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?

@leonard84
Copy link
Member

@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
      } 

@vanta
Copy link

vanta commented Jan 14, 2019

@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.

@EduardoSolanas
Copy link

EduardoSolanas commented Jan 14, 2019

@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

@leonard84
Copy link
Member

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.

@vanta
Copy link

vanta commented Jan 14, 2019

@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.

@leonard84
Copy link
Member

@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.

@EduardoSolanas
Copy link

@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

@huehnerlady
Copy link

I created a new issue for that behaviour now:
#962

BoukeNijhuis added a commit to BoukeNijhuis/spock that referenced this issue Sep 26, 2020
By default Spock uses a match first algorithm to determine the defined return value of a method. So whenever there are multiple defined return values, it will pick the first (that is not exhausted). This change enables a user to change this to a match last algorithm. So Spock will pick the last defined return value (that is not exhausted). This enables easy overriding of default return values. As requested in issue spockframework#26, spockframework#251, spockframework#321 and spockframework#962.
@CamilYed
Copy link

CamilYed commented Apr 7, 2021

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests