Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Spocklight: Custom Default Responses for Stubs

DZone's Guide to

Spocklight: Custom Default Responses for Stubs

Spock has found great popularity in the BDD crowd over the last few years. Take a look at this hands-on example of stubbing in the popular framework.

· DevOps Zone
Free Resource

Download “The DevOps Journey - From Waterfall to Continuous Delivery” to learn learn about the importance of integrating automated testing into the DevOps workflow, brought to you in partnership with Sauce Labs.

Although I couldn't make it to Gr8Conf EU this year, I am glad that a lot of the presentations are available as slide decks and videos. The slide deck for the talk Interesting nooks and crannies of Spock you (may) have never seen before by Marcin Zajączkowski is very interesting. This is really a must-read if you use Spock (and why shouldn't you?) in your projects. One of the interesting things is the ability to change the response for methods in a class that is stubbed using Spock's Stub method, but that have no explicit stubbed method definition.

When we create a stub, we normally add code that implements the methods from the stubbed class. In our specification, the methods we have written are invoked instead of the original methods from the stubbed class. By default, if we don't override a method definition, but it is used in the specification, Spock will try to create a response using a default response strategy. The default response strategy for a stub is implemented by the class EmptyOrDummyResponse. For example, if a method has a return type Message , then Spock will create a new instance of Message and return it to be used in the specification. Spock also has a ZeroOrNullResponse response strategy. With this strategy, null is returned for our method, which returns the Message type.

Both response strategies implement the IDefaultResponse interface. We can write our own response strategy by implementing this interface. When we use the Stub method, we can pass an instance of our response strategy with the defaultResponse named argument of the method. An example: MessageProvider stub = Stub(defaultResponse: new CustomResponse()). We implement the respond method of IDefaultResponse to write a custom response strategy. The method gets an IMockInvocation instance. We can use this instance to check for example the method name, return type, arguments, and more. Based on this, we can write code to return the response we want.

In the following example we have a Spock specification where we create a stub using the default response strategy, the ZeroOrNullResponse strategy, and a custom-written response strategy:

package com.mrhaki.spock

@Grab('org.spockframework:spock-core:1.0-groovy-2.4')
import spock.lang.Specification
import spock.lang.Subject
import org.spockframework.mock.ZeroOrNullResponse
import org.spockframework.mock.IDefaultResponse
import org.spockframework.mock.IMockInvocation

class SampleSpec extends Specification {

    def """stub default response returns
           instance of Message created with default constructor"""() {
        given: 'Use default response strategy EmptyOrDummyResponse'
        final MessageProvider messageProvider = Stub()
        final Sample sample = new Sample(messageProvider)

        expect:
        sample.sampleMessage == 'Sample says: default'
    }

    def "stub default reponse returns null with ZeroOrNullResponse"() {
        given: 'Use default response strategy of ZeroOrNullResponse'
        final MessageProvider messageProvider =
                Stub(defaultResponse: ZeroOrNullResponse.INSTANCE)
        final Sample sample = new Sample(messageProvider)

        when:
        sample.sampleMessage

        then: 'messageProvider.message returns null'
        thrown(NullPointerException)
    }

    def """stub default response returns
           Message object with initialized text property
           from StubMessageResponse"""() {
        given: 'Use custom default response strategy'
        final MessageProvider messageProvider =
                Stub(defaultResponse: new StubMessageResponse())
        final Sample sample = new Sample(messageProvider)

        expect:
        sample.sampleMessage == 'Sample says: *STUB MESSAGE TEXT*'
    }

}

/**
 * Class to test with a dependency on MessageProvider
 * that is stubbed in the specification.
 */
class Sample {
    private final MessageProvider messageProvider

    Sample(final MessageProvider messageProvider) {
        this.messageProvider = messageProvider
    }

    String getSampleMessage() {
        "Sample says: ${messageProvider.message.text}"
    }

    String sampleMessage(String prefix) {
        "Sample says: ${messageProvider.getMessageWithPrefix(prefix).text}"
    }
}

/**
 * Work with messages. This interface is stubbed
 * in the specification.
 */
interface MessageProvider {
    Message getMessage()
    Message getMessageWithPrefix(String prefix)
}

/**
 * Supporting class for MessageProvider.
 */
class Message {
    String text = 'default'
}

/**
 * Custom default response strategy.
 * When a method has a Message return type then we
 * create an instance of Message with a custom text
 * property value.
 * Otherwise rely on default behaviour.
 */
class StubMessageResponse implements IDefaultResponse {
    @Override
    Object respond(IMockInvocation invocation) {
        // If return type of method is Message we create
        // a new Message object with a filled text property.
        if (invocation.method.returnType == Message) {
            return new Message(text: '*STUB MESSAGE TEXT*')
        }

        // Otherwise use default response handler for Stubs.
        return ZeroOrNullResponse.INSTANCE.respond(invocation)
    }
}

Written with Spock 1.0-groovy-2.4.

Discover how to optimize your DevOps workflows with our cloud-based automated testing infrastructure, brought to you in partnership with Sauce Labs

Topics:
spock ,spocklight ,java

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}