Ratpacked: Override Registry Objects With Mocks In Integration Specifications
Testing a Ratpack application is not difficult.
Ratpack has excellent support for writing unit and integration tests.
When we create the fixture MainClassApplicationUnderTest
we can override the method addImpositions
to add mock objects to the application.
We can add them using the ImpositionsSpec
object.
When the application starts with the test fixture the provided mock objects are used instead of the original objects.
For a Groovy based Ratpack application we can do the same thing when we create the fixture GroovyRatpackMainApplicationUnderTest
.
We start with a simple Java Ratpack application.
The application adds an implementation of a NumberService
interface to the registry.
A handler uses this implementation for rendering some output.
package mrhaki.ratpack;
import ratpack.registry.Registry;
import ratpack.server.RatpackServer;
public class RatpackApplication {
public static void main(String[] args) throws Exception {
RatpackServer.start(server -> server
// Add a implementation of NumberService interface
// to the registry, so it can be used by the handler.
.registry(Registry.single(NumberService.class, new NumberGenerator()))
// Register a simple handler to get the implementation
// of the NumberService interface, invoke the give() method
// and render the value.
.handler(registry -> ctx -> ctx
.get(NumberService.class).give()
.then(number -> ctx.render(String.format("The answer is: %d", number)))));
}
}
The NumberService
interface is not difficult:
package mrhaki.ratpack;
import ratpack.exec.Promise;
public interface NumberService {
Promise give();
}
The implementation of the NumberService
interface returns a random number:
package mrhaki.ratpack;
import ratpack.exec.Promise;
import java.util.Random;
public class NumberGenerator implements NumberService {
@Override
public Promise give() {
return Promise.sync(() -> new Random().nextInt());
}
}
To test the application we want to use a mock for the NumberService
interface.
In the following specification we override the addImpositions
method of the MainClassApplicationUnderTest
class:
package mrhaki.ratpack
import ratpack.exec.Promise
import ratpack.impose.ImpositionsSpec
import ratpack.impose.UserRegistryImposition
import ratpack.registry.Registry
import ratpack.test.MainClassApplicationUnderTest
import ratpack.test.http.TestHttpClient
import spock.lang.AutoCleanup
import spock.lang.Specification
/**
* Integration test for the application.
*/
class RatpackApplicationSpec extends Specification {
/**
* Mock implementation for the {@link NumberService}.
*/
private final NumberService mockNumberService = Mock()
/**
* Setup {@link RatpackApplication} for testing and provide
* the {@link #mockNumberService} instance to the Ratpack registry.
*/
@AutoCleanup
private aut = new MainClassApplicationUnderTest(RatpackApplication) {
@Override
protected void addImpositions(final ImpositionsSpec impositions) {
// Set implementation of NumberService interface to
// our mock implementation for the test.
impositions.add(
UserRegistryImposition.of(
Registry.single(NumberService, mockNumberService)))
}
}
/**
* Use HTTP to test our application.
*/
private TestHttpClient httpClient = aut.httpClient
void 'render output with number'() {
when:
final response = httpClient.get()
then:
// Our mock should get invoked once and we return
// the fixed value 42 wrapped in a Promise.
1 * mockNumberService.give() >> Promise.sync { 42 }
and:
response.statusCode == 200
response.body.text == 'The answer is: 42'
}
}
Written with Ratpack 1.4.5.