'How to reset a method mock in Spock?

I had a mocked bean in a configuration:

@Profile('test')
@Configuration
class TestSecurityConfig {
  private final mockFactory = new DetachedMockFactory()
  @Primary @Bean
  AuthUserDetailsProvider authUserDetailsProvider() {
      mockFactory.Stub(AuthUserDetailsProvider)
  }
}

By default, I mock a method call is setup() method for all tests.

def setup() {
    authProvider.authenticationPrincipal >> random(authUserDetails)
}

By in a specific test, I need to override the method mock

def 'exercise missed user processing'() {
    given: 'no user data'
    authProvider.authenticationPrincipal >> emptyUser
    ...
}

But it doesn't work. How can I override a method mocking in Spock? Is there any way to reset mock interceptions manually?



Solution 1:[1]

Interactions defined in then blocks will have precedence over those defined in given/setup, for the preceding when block. Normally, you pair them with assertions, i.e., 1 * ... but it works for plain stubbing too.

import spock.lang.*

class ASpec extends Specification {
  List myList = Mock()
    
  def setup() {
    myList.get(0) >> 1
  }
    
  def "default behavior"() {
    expect: 
    myList.get(0) == 1
  }
    
  def "override behavior"() {
    when:
    def result = myList.get(0)

    then:
    myList.get(0) >> 42
    result == 42
  }
}

So in your case:

def 'exercise missed user processing'() {
    when:
    ...

    then:
    1 * authProvider.authenticationPrincipal >> emptyUser
    ...
}

Solution 2:[2]

But it doesn't work?

Whatever you put in setup will be executed before each test. If you don't want that, then the code probably shouldn't be in setup.

How can I override a method mocking in Spock?

If by "override" you mean undo what was done in setup and then do something else, I am not aware of a way to do that.

Is there any way to reset mock interceptions manually?

Not that I am aware of. It is unusual to tell the mock that you want a certain behavior and later tell the mock that you have changed your mind.

Solution 3:[3]

The answer from @leonard-brünings is quite helpful, but I feel that it missed to explain the below part of the question with a more direct and simple explanation:

But it doesn't work.

The Where to Declare Interactions section has the explanation:

When an invocation on a mock object occurs, it is matched against interactions in the interactions' declared order. If an invocation matches multiple interactions, the earliest declared interaction that hasn’t reached its upper invocation limit will win.

Because Spock sees the interaction in setup before the given: section, what this means is that the one defined in setup takes precedence.

How can I override a method mocking in Spock?

This is already explained and shown in the answer from @leonard-brünings, but just want to quote the relevant section from the same above documentation:

There is one exception to this rule: Interactions declared in a then: block are matched against before any other interactions. This allows to override interactions declared in, say, a setup method with interactions declared in a then: block.

Which is why the solution is to move the interaction from the given: block to then: block.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 kriegaex
Solution 2 Jeff Scott Brown
Solution 3 haridsv