xetra11
xetra11

Reputation: 8837

Stubbed method should return value depending on given mock parameter in Spock

I would like to have different returning results - depending on the given mocked parameter of a method. Please consider the following code snippet to follow my intention

class ExampleSpec extends Specification {

    def "should return second value of list of return values"() {
        given:
        Person personBob = Mock()
        Person personJackson = Mock()
        PersonHelper stubbedPerson = Stub()

        stubbedPerson.getNameOfBrother(personBob) >> "Billy Bob";
        stubbedPerson.getNameOfBrother(personJackson) >> "Tommy Jackson";

        when:
        String actual = stubbedPerson.getNameOfBrother(personBob)
        String actual2 = stubbedPerson.getNameOfBrother(personJackson)

        then:
        actual == "Billy Bob" // true
        actual2 == "Tommy Jackson" // false "Billy Bob"
    }

}

The test fails because the second call for var actual2 still return Billy Bob rather than Tommy Jackson. I know there is a way to return different values by call order but I would like to make it dependend on given mocks.

Using normal values - no Mock/Stub Proxies - as parameter values does actually work. I assume that the Spock engine can not differ between two mocks. But I am unsure about this, because the proxies do have IDs as instance fields.

Upvotes: 1

Views: 5231

Answers (1)

Szymon Stepniak
Szymon Stepniak

Reputation: 42184

For the record - stubbing with mock objects works. I've added simple Person and PersonHelper classes to your example and the test passes:

import spock.lang.Specification

class ExampleSpec extends Specification {

    def "should return second value of list of return values"() {
        given:
        Person personBob = Mock()
        Person personJackson = Mock()
        PersonHelper stubbedPerson = Stub()

        stubbedPerson.getNameOfBrother(personBob) >> "Billy Bob";
        stubbedPerson.getNameOfBrother(personJackson) >> "Tommy Jackson";

        when:
        String actual = stubbedPerson.getNameOfBrother(personBob)
        String actual2 = stubbedPerson.getNameOfBrother(personJackson)

        then:
        actual == "Billy Bob" // true
        actual2 == "Tommy Jackson" // false "Billy Bob"
    }

    static class Person {
        String name
    }

    static class PersonHelper {
        String getNameOfBrother(Person person) {
            return null
        }
    }
}

I have checked it with spock-core:1.1-groovy-2.4, spock-core:1.0-groovy-2.4 and even spock-core:0.7-groovy-2.0. All worked.

But what is even more important - such test does not make any sense. You don't test your code at all. You only test if mocking framework mocks correctly. This test could make some sense if you use Spock mocks in your production code, but this is not a valid assumption.

What may go wrong?

Think for a second about this test. According to your when: block you are trying to test if PersonHelper.getNameOfBrother(Person person) returns a valid name of a brother for two different objects. Now let's assume that this is the only test of a PersonHelper class in your project. Imagine what happens if suddenly implementation of getNameOfBrother method starts throwing NullPointerException for some random reason. Ask yourself - does your unit test protects you from such situation? Nope. Your test always passes, because you are stubbing the method you are testing. If you test a real implementation instead and you pass a real Person object then you will get notified about NullPointerException in the test. Otherwise you will see it when you deploy your code and some user's action calls this method and it fails.

Upvotes: 3

Related Questions