Skip to content

Testing

Guicey provides test extensions for:

Deprecated:

Almost all extensions implemented with DropwizardTestSupport.

History

There is no special Spock 2 extensions, instead junit 5 extensions must be used directly. You get the best of both worlds - use junit extensions (and so can always easily migrate to pure junit) and have spock (and groovy) expressiveness.

Currently, spock 1 and junit 4 extensions considered deprecated becuase they use deprecated dropwizard rule.

Tip

Test framework-agnostic utilities are useful with junit 5 or spock extensions in cases when assertions required after application shutdown or to test application startup errors.

Additionally, guicey provides several mechanisms at its core for application customization in tests (see below).

Test concepts

Core dropwziard testing support proposes atomic testing approach (separate testing of each element).

With DI (guice) we have to move towards integration testing because:

  1. It is now harder to mock classes "manually" (because of DI "black box")
  2. We have a core (guice injector, without web services), starting much faster than complete application.

The following kinds of tests should be used:

  1. Unit tests for atomic parts (usually, utility classes)
  2. Core integration tests: lightweight application start to test core services (business logic)
  3. Web integration tests: full application startup to test web endpoints (full workflow to check transport layer)
  4. Custom command tests (also kind of "integration", but depends on command)
  5. Application startup fail test (done with command runner) to check self-checks

Setup objects

Junit 5 extensions provide support for setup objects: a simple way to prepare test environment and apply context configuration (e.g. start test database).

Spock 2 test could also use setup objects.

Configuration hooks

Guicey provides hooks mechanism to be able to modify application configuration in tests.

Using hooks you can disable installers, extensions, guicey bundles
or override guice bindings.

It may also be useful to register additional extensions (e.g. to validate some internal behaviour).

Example hook:

public class MyHook implements GuiceyConfigurationHook {

    public void configure(GuiceBundle.Builder builder) {
        builder
            .disableModules(FeatureXModule.class)
            .disable(inPackage("com.foo.feature"))
            .modulesOverride(new MockDaoModule())
            .option(Myoptions.DebugOption, true);
    }
}

Note

You can modify options in hook and so could enable some custom debug/monitoring options specifically for test.

There are special spock and junit extensions for hooks registrations.

Disables

You can use hooks to disable all not needed features in test:

This way you can isolate (as much as possible) some feature for testing.

The most helpful should be bundles disable (if you use bundles for features grouping) and guice modules.

Use predicate disabling.

Note

It is supposed that disabling will be used instead of mocking - you simply remove what you don't need and register replacements, if required.

Guice bindings override

It is quite common requirement to override bindings for testing. For example, you may want to mock database access.

Guicey could use guice Modules.override() to help you override required bindings. To use it prepare module only with changed bindings (bindings that must override existing). For example, you want to replace ServiceX. You have few options:

  • If it implements interface, implement your own service and bind as bind(ServiceContract.class).to(MyServiceXImpl.class)
  • If service is a class, you can modify its behaviour with extended class bind(ServiceX.class).to(MyServiceXExt.class)
  • Or you can simply register some mock instance bind(ServiceX.class).toInstance(myMockInstance)
public class MyOverridingModule extends AbstractModule {

    protected configure() {
        bind(ServiceX.class).to(MyServiceXExt.class);        
    }
}

And register overriding module in hook:

public class MyHook implements GuiceyConfigurationHook {
    public void configure(GuiceBundle.Builder builder) {
        builder
            .modulesOverride(new MyOverridingModule());
    }
}

Debug bundles

You can also use special guicey bundles, which modify application behavior. Bundles could contain additional listeners or services to gather additional metrics during tests or validate behavior.

For example, guicey tests use bundle to enable restricted guice options like disableCircularProxies.

Bundles are also able to:

  • disable installers, extensions, guice modules
  • override guice bindings

You can also use lookup mechanism to load bundles in tests. For example, system properties lookup.

Overriding overridden beans

Guicey provides direct support for overriding guice bindings, so in most cases you don't need to do anything.

But, if you use this to override application bindings need to override such bindings in test (again), then you may use provided custom injector factory:

Register factory in guice bundle:

GuiceBundle.builder()
    .injectorFactory(new BindingsOverrideInjectorFactory())

After that you can register overriding bindings (which will override even modules registered in modulesOverride) with:

BindingsOverrideInjectorFactory.override(new MyOverridingModule())

Important

It is assumed that overriding modules registration and application initialization will be at the same thread (ThreadLocal used for holding registered modules to allow parallel tests usage).

For example, suppose we have some service CustomerService and it's implementation CustomerServiceImpl, defined in some 3rd party module. For some reason we need to override this binding in the application:

public class OverridingModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(CustomerService.class).to(CustomCustomerServiceImpl.class);
    }
}

If we need to override this binding in test (again):

(Simplified) registration looks like this:

GuiceBundle.builder()
    .injectorFactory(new BindingsOverrideInjectorFactory())
    .modules(new ThirdPatyModule())
    // override binding for application needs
    .modulesOverride(new OverridingModule())
    ...
    .build()

// register overriding somewhere
BindingsOverrideInjectorFactory.override(new TestOverridingModule())    

Tip

Configuration hook may be used for static call (as a good integration point)

After test startup, application will use customer service binding from TestOverridingModule.

Test commands

Dropwizard commands could be tested with commands test support

For example:

CommandResult result = TestSupport.buildCommandRunner(App.class)
        .run("simple", "-u", "user")

Assertions.assertTrue(result.isSuccessful());

There are no special junit 5 or spock 2 extensions for command tests because general run methods are already the best way.

Test application startup fail

To verify application self-check mechanisms (make sure application would fail with incomplete configuration, or whatever other reason) use commands runner.

For example:

CommandResult result = TestSupport.buildCommandRunner(App.class)
        .runApp()