Skip to content

Junit 4

Warning

Since guicey 5.5 junit 4 support was extracted from guicey to external module:

  • Package remains the same to simplify migration (only additional dependency would be required)
  • Deprecation marks removed from rules to reduce warnings.

DEPRECATED because dropwizard deprecated its junit4 rules. Consider migration to JUnit 5

Setup

Required dependencies (assuming BOM used for versions management):

testImplementation 'ru.vyarus.guicey:guicey-test-junit4'

With Junit 5

OR you can use it with junit 5 vintage engine:

testImplementation 'ru.vyarus.guicey:guicey-test-junit4'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'

This way all existing junit 4 tests would work and new tests could use junit 5 extensions.

Rules

Provided rules:

  • GuiceyAppRule - lightweight integration tests (guice only)
  • GuiceyHooksRule - test-specific application modifications
  • StartupErrorRule - helper for testing failed application startup

Testing core logic

For integration testing of guice specific logic you can use GuiceyAppRule. It works almost like DropwizardAppRule, but doesn't start jetty (and so jersey and guice web modules will not be initialized). Managed and lifecycle objects supported.

public class MyTest {

    @Rule
    GuiceyAppRule<MyConfiguration> RULE = new GuiceyAppRule<>(MyApplication.class, "path/to/configuration.yaml");

    public void testSomething() {
        RULE.getBean(MyService.class).doSomething();
        ...
    }
}

As with dropwizard rule, configuration is optional

new GuiceyAppRule<>(MyApplication.class, null)

Testing web logic

For web component tests (servlets, filters, resources) use DropwizardAppRule.

To access guice beans use injector lookup:

InjectorLookup.getInstance(RULE.getApplication(), MyService.class).get();

Customizing guicey configuration

Guicey provides a way to modify its configuration in tests. You can apply configuration hook using rule:

// there may be exact class instead of lambda
new GuiceyHooksRule((builder) -> builder.modules(...))

To use it with DropwizardAppRule or GuiceyAppRule you will have to apply explicit order:

static GuiceyAppRule RULE = new GuiceyAppRule(App.class, null);
@ClassRule
public static RuleChain chain = RuleChain
       .outerRule(new GuiceyHooksRule((builder) -> builder.modules(...)))
       .around(RULE);

Attention

RuleChain is required because rules execution order is not guaranteed and configuration rule must obviously be executed before application rule.

If you need to declare configurations common for all tests then declare rule instance in base test class and use it in chain (at each test):

public class BaseTest {
    // IMPORTANT no @ClassRule annotation here!
     static GuiceyHooksRule BASE = new GuiceyHooksRule((builder) -> builder.modules(...))
 }

 public class SomeTest extends BaseTest {
     static GuiceyAppRule RULE = new GuiceyAppRule(App.class, null);
     @ClassRule
     public static RuleChain chain = RuleChain
        .outerRule(BASE)
        // optional test-specific staff
        .around(new GuiceyHooksRule((builder) -> builder.modules(...)) 
        .around(RULE);
 }

Warning

Don't use configuration rule with spock because it will not work. Use special spock extension instead.

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)
    }                    
}

Testing startup errors

If exception occur on startup dropwizard will call System.exit(1) instead of throwing exception (as it was before 1.1.0). System exit could be intercepted with system rules.

Note

If you are going to move to junit 5 soon then better use system stubs library. It is a successor of system rules and provides both junit 4 and 5 extensions.

Special rule provided to simplify work with system rules: StartupErrorRule. It's a combination of exit and out/err outputs interception rules.

public class MyErrTest {

    @Rule
    public StartupErrorRule RULE = StartupErrorRule.create();

    public void testSomething() {
        new MyErrApp().main('server');
    }
}

This test will pass only if application will throw exception during startup.

In junit it is impossible to apply checks after exit statement, so such checks must be registered as a special callback:

public class MyErrTest {

    @Rule
    public StartupErrorRule RULE = StartupErrorRule.create((out, err) -> {
        Assert.assertTrue(out.contains("some log line"));
        Assert.assertTrue(err.contains("expected exception message"));
    });

    public void testSomething() {
        new MyErrApp().main('server');
    }
}

Note that err will contain full exception stack trace and so you can check exception type too by using contains statement like above.

Check callback(s) may be added after rule creation:

@Rule
public StartupErrorRule RULE = StartupErrorRule.create();

public void testSomething() throws Exception {
    RULE.checkAfterExit((out, err) -> {
            Assert.assertTrue(err.contains("expected exception message"));
        });
    ...
}

Multiple check callbacks may be registered (even if the first one was registered in rule's create call).

Rule works a bit differently with spock 1.

Migrating to JUnit 5

In essence:

  • Use annotations instead of rules (and forget about RuleChain difficulties)
  • Test fields injection will work out of the box, so no need for additional hacks
  • JUnit 5 propose parameter injection, which may be not common at first, but it's actually very handy

Also, there is a pre-configured http client suitable for calling test application urls (or any other general url).