Testing

You can use all existing dropwizard testing tools for unit tests.

Guicey tests

Guicey intended to shine in integration tests: it provides a lot of tools for application modification.

The most important is hooks mechanism which allows you to re-configure existing application. There are two main testing approaches:

  • Disable everything not required and register custom versions instead
  • Override some bindings (pure guice Modules.override() method)

Warning

Guicey currently provides Junit 4 and spock extensions. Junit 5 support will be added soon.

Disable and replace

Suppose we have the following application:

public class App extends Application<MyConfig> {
    public void initialize(Bootstrap<MyConfig> bootstrap) {
        bootstrap.addBundle(GuiceBundle.builder()
            .bundles(new SecurityOnlyBundle())
            .modules(new ServiceModule(), new DaoModule())
            .build()
    }
}

Specifically for tests we create special module MockDaoModule which applies all the same bindings, but with mock implementations.

Just for demonstration, suppose that application registers SecurityOnlyBundle which do nothing except of additional security. Suppose we don't need this in tests.

public class MyTest {    

    // NOTE! no rule marker here!
    static DropwizardAppRule<TestConfiguration> RULE = new DropwizardAppRule<>(App.class); 

    @ClassRule 
    static RuleChain chain = RuleChain
       .outerRule(new GuiceyHooksRule((builder) -> 
                                builder.disableBundles(SecurityOnlyBundle.class)
                                       .disableModules(DaoModule.class)
                                       .modules(new MockDaoModule())))
       .around(RULE);   

}

Here dropwizard rule used for application startup and hooks rule used around dropwizard rule to re-configure application:

  • remove SecurityOnlyBundle
  • remove DaoModule
  • add MockDaoModule

Note that this way you can disable everything: module, extensions, guicey and dropwizard bundles and installers.

Note

Bundles (both guice and dropwizard) and guice modules are actually hierarchical (one bundle/module can register other bundle/module) and you can disable even exact bundle/module inside this hierarchy (not just directly registered). See more: about guice transitive bundles and dropwizard transitive bundles

All disables are shown on diagnostic report - you can use it to verify configuration state.

Override bindings

We can do the same without replacing module, but overriding bindings using guice Modules.override() feature. This is preferred in cases when modules are not so well structured and you need to override just a subset of bindings (not all bindings in module).

Above example would look like:

public class MyTest {    

    // NOTE! no rule marker here!
    static DropwizardAppRule<TestConfiguration> RULE = new DropwizardAppRule<>(App.class); 

    @ClassRule 
    static RuleChain chain = RuleChain
       .outerRule(new GuiceyHooksRule((builder) -> 
                                builder.disableBundles(SecurityOnlyBundle.class)                                       
                                       .modulesOverride(new MockDaoModule())))
       .around(RULE);   

}

In the previous example all bindings from DaoModule were removed and here we just register overriding bindings so bindings from MockDaoModule will be used instead of (the same) bindings from DaoModule.

Note

All overrides are visible on guice report - use it to verify override correctness.

Configuration

Pure dropwizard staff. Just to pay attention.

For tests use custom configuration file (e.g. src/test/resources/test-config.yml).

@ClassRule
static DropwizardAppRule<TestConfiguration> RULE =
        new DropwizardAppRule<>(MyApp.class, ResourceHelpers.resourceFilePath("test-config.yaml"));

Override exact value:

 static DropwizardAppRule<TestConfiguration> RULE =
            new DropwizardTestSupport<>(MyApp.class,
                null,  // main config may or may not be declared
                ConfigOverride.config("server.applicationConnectors[0].port", "0") 
            );

Access guice beans

When using DropwizardAppRule the only way to obtain guice managed beans is through:

InjectorLookup.getInjector(RULE.getApplication()).getBean(MyService.class);

Also, the following trick may be used to inject test fields:

public class MyTest {

    @ClassRule
    static DropwizardAppRule<TestConfiguration> RULE = ...

    @Inject MyService service;
    @Inject MyOtherService otherService;

    @Before
    public void setUp() {
        InjectorLookup.get(RULE.getApplication()).get().injectMemebers(this)
    }                    
}

Lightweight tests

DropwizardAppRule runs complete application, including web context (requires open ports). But in many cases, you just need a working Injector to check core application logic.

For such cases, guicey provides lightweight rule GuiceyAppRule. In contrast to DropwizardAppRule it:

  • will not start jetty (no ports bind, no HK2 launched)
  • start Managed objects to simulate lifecycle

These tests work much-faster!

public class MyTest {    

    // NOTE! no rule marker here!
    static GuiceyAppRule<TestConfiguration> RULE = new GuiceyAppRule<>(App.class); 

    @ClassRule 
    static RuleChain chain = RuleChain
       .outerRule(new GuiceyHooksRule((builder) -> 
                                builder.disableBundles(SecurityOnlyBundle.class)                                       
                                       .modulesOverride(new MockDaoModule())))
       .around(RULE);   


    @Test
    public void test() {
         RULE.getBean(MyService.class).doSomething();
         ...  
    }       
}

Note that GuiceyAppRule provides direct accessor for guice beans.

Startup fail tests

If you test all cases, even crashing application startup (e.g. due to mis-configuration) then use special startup errors rule which intercepts System.exit(1) call performed by dropwizard, allowing you to assert failure.

Spock

You can use groovy-based Spock framework instead of junit. Spock tests are much easier to write (you can write less code) and more expressive.

Guicey provides all required extensions to write tests above with spock. For example, the first example will look like:

@UseDropwizardApp(value = App, hooks = [Hook])
class MyTest extends Specification {    

    @Inject
    MyService service

    def 'Check service method'() {

        when: 'calling service method'
        def res = service.doSoomething()
        then: 'value is correct'
        res == 12
    }

    // spock 2.0 will support java 8 lambdas (through just released groovy 3)
    // untill then using class-based declaration      
    static class Hook implements GuiceyConfigurationHook {
        void configure(GuiceBundle.Builder builder) {
            builder.disableBundles(SecurityOnlyBundle.class)
                   .disableModules(DaoModule.class)
                   .modules(new MockDaoModule())            
        }
    }    
}

Tip

If hooks are common for all tests, then they could be moved to base class:

@UseGuiceyHooks(Hook)
abstract class BaseTest extends Specification {
    // hook class here 
}

@UseDropwizardApp(App)
class MyTest extends BaseTest { 

Note that in spock tests fields injection in test works automatically.

Lightweight guicey tests are available through @UseGuiceyApp annotation.

Overriding configuration properties:

@UseDropwizardApp(value = App,
        config = 'src/test/resources/test-config.yml',
        configOverride = [
                @ConfigOverride(key = "foo", value = "2"),
                @ConfigOverride(key = "bar", value = "12")
        ])
class DWConfigOverrideTest extends Specification { ... }

Hint

All guicey tests written in spock, so you can see them and decide what framework is better fits your needs.