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.
// File: src/main/java/mrhaki/ratpack/RatpackApplication.java 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:
// File: src/main/java/mrhaki/ratpack/NumberService.java package mrhaki.ratpack; import ratpack.exec.Promise; public interface NumberService { Promise<Integer> give(); }
The implementation of the NumberService
interface returns a random number:
// File: src/main/java/mrhaki/ratpack/NumberGenerator.java package mrhaki.ratpack; import ratpack.exec.Promise; import java.util.Random; public class NumberGenerator implements NumberService { @Override public Promise<Integer> 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:
// File: src/test/groovy/mrhaki/ratpack/RatpackApplicationSpec.groovy 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.