Skip to content

Testing application

Guicey provides two junit extensions:

@TestGuiceyApp assumed to be used for the majority of tests as it only starts guice injector (which is much faster than complete application startup). Such tests are ideal for testing business logic (services).

@TestDropwizardApp (full integration test) used only to check web endpoints and full workflow (assuming all business logic was already tested with lightweight tests)

Both extensions:

Field annotations:

  • @EnableHook - hooks registration
  • @EnableSetup - setup objects registration
  • @StubBean - guice bean stubs registration
  • @MockBean - guice bean mocks registration (mockito)
  • @SpyBean - guice bean spies registration (mockito)
  • @TrackBean - guice beans execution tracking (simpler then mockito spies; suitable for performance testing)
  • @StubRest - lightweight REST testing
  • @RecordLogs - logs testing

Method parameter annotations:

Testing core logic

@TestGuiceyApp creates guice injector (runs all application services) without starting jetty (so resources, servlets and filters will not be available). Managed objects will still be handled correctly.

@TestGuiceyApp(MyApplication.class)
public class AutoScanModeTest {

    @Inject 
    MyService service;

    @Test
    public void testMyService() {        
        Assertions.assertEquals("hello", service.getSmth());     
    }

Also, injections work as method parameters:

@TestGuiceyApp(MyApplication.class)
public class AutoScanModeTest {

    public void testMyService(MyService service) {        
        Assertions.assertEquals("hello", service.getSmth());     
    }

Application started before all tests in annotated class and stopped after them.

Annotation options

Option Description Default
config Configuration file path ""
configOverride Configuration file overriding values {}
configModifiers Configuration object modifier {}
hooks Hooks to apply {}
setup Setup objects to apply {}
injectOnce Inject test fields just one for multiple test methods with one test instance false
debug Enable extension debug output false
reuseApplication Use the same application instance for multiple tests false
useDefaultExtensions Use default guicey field extensions true
clientFactory Custom client factory to use DefaultTestClientFactory
managedLifecycle Managed beans lifecycle simulation true

Managed lifecycle

Core application tests (@TestGuiceyApp) does not start web part and so lifecycle should not work, but Managed objects often used to initialize core services.

Guicey core test simulate Managed lifecycle (call start and stop methods). For tests, not requiring lifecycle at all, it might be disabled with:

@TestGuiceyApp(value = App.class, managedLifecycle = false)

or

@RegisterExtension
static TestGuiceyAppExtension ext = TestGuiceyAppExtension.forApp(..)
        ...
        .disableManagedLifecycle()

Note

Application lifecycle will remain: events like onApplicationStartup would still be working (and all registered LifeCycle objects would work). Only managed objects ignored.

Testing web logic

@TestDropwizardApp is useful for complete integration testing (when web part is required):

@TestDropwizardApp(MyApplication.class)
class WebModuleTest {

    @Inject 
    MyService service

    @Test
    public void checkWebBindings(ClientSupport client) {

        Assertions.assertEquals("Sample filter and service called", 
            client.targetMain("servlet").request().buildGet().invoke().readEntity(String.class));

        Assertions.assertTrur(service.isCalled());

@TestDropwizardApp contains the same annotation options as core test, but without lifecycle simulation (lifecycle managed by started server).

Random ports

In order to start application on random port you can use configuration shortcut:

@TestDropwizardApp(value = MyApplication.class, randomPorts = true)

Note

Random ports setting override exact ports in configuration:

@TestDropwizardApp(value = MyApplication, 
                  config = 'path/to/my/config.yml', 
                  randomPorts = true)
Also, random ports support both server types (default and simple)

Real ports could be resolved with ClientSupport object.

Rest mapping

Normally, rest mapping configured with server.rootMapping=/something/* configuration, but if you don't use custom configuration class, but still want to re-map rest, shortcut could be used:

@TestDropwizardApp(value = MyApplication.class, restMapping="something")

In contrast to config declaration, attribute value may not start with '/' and end with '/*' - it would be appended automatically.

This option is only intended to simplify cases when custom configuration file is not yet used in tests (usually early PoC phase). It allows you to map servlet into application root in test (because rest is no more resides in root). When used with existing configuration file, this parameter will override file definition.

Alternative declaration

Both extensions could be declared in fields:

@RegisterExtension
static TestDropwizardAppExtension app = TestDropwizardAppExtension.forApp(AutoScanApplication.class)
        .config("src/test/resources/ru/vyarus/dropwizard/guice/config.yml")
        .configOverrides("foo: 2", "bar: 12")
        .randomPorts()
        .hooks(Hook.class)
        .hooks(builder -> builder.disableExtensions(DummyManaged.class))
        .create();

The only difference with annotations is that you can declare hooks and setup objects as lambdas directly (still hooks in static fields will also work).

@RegisterExtension
static TestGuiceyAppExtension app = TestGuiceyAppExtension.forApp(AutoScanApplication.class)
        ...

This alternative declaration is intended to be used in cases when guicey extensions need to be aligned with other 3rd party extensions: in junit you can order extensions declared with annotations (by annotation order) and extensions declared with @RegisterExtension (by declaration order). But there is no way to order extension registered with @RegisterExtension before annotation extension.

So if you have 3rd party extension which needs to be executed BEFORE guicey extensions, you can use field declaration.

Note

Junit 5 intentionally shuffle @RegisterExtension extensions order, but you can always order them with @Order annotation.

Start application per test method

When you declare extensions with annotations or with @RegisterExtension in static fields, application would be started before all test methods and shut down after last test method.

If you want to start application for each test method then declare extension in non-static field:

@RegisterExtension
TestGuiceyAppExtension ext = TestGuiceyAppExtension.forApp(App.class).create()

// injection would be re-newed for each test method
@Inject Bean bean;

@Test
public void test1() {
    Assertions.assertEquals(0, bean.value);
    // changing value to show that bean was reset between tests
    bean.value = 10    
}

@Test
public void test2() {
    Assertions.assertEquals(0, bean.value);
    bean.value = 10
}

Also, @EnableHook and @EnableSetup fields might also be not static (but static fields would also work) in this case:

@RegisterExtension
TestGuiceyAppExtension ext = TestGuiceyAppExtension.forApp(App.class).create()

@EnableSetup
MySetup setup = new MySetup()