Search code examples
junitdependency-injectionguiceratpack

JUnit testing of Ratpack server with Guice injection


I am trying to write a JUnit test with service dependencies being injected.

protected MainClassApplicationUnderTest aut = new MainClassApplicationUnderTest(App.class) { 
    @Override 
    protected void addImpositions(final ImpositionsSpec impositions) { 
        impositions.add(UserRegistryImposition.of(appRegistry -> { 
            // Allow modifying Injector in tests 
            return appRegistry.join(Guice.registry(injector)); 
        })); 
    } 
}; 

private Injector injector = com.google.inject.Guice.createInjector(new Module()); 

@Before 
public void setup () { 
    injector.injectMembers(this); 
} 

@After 
public void tearDown() { 
    aut.close(); 
} 

and then using injected services in my test classes:

@Inject 
private UserService userService; 

This was working fine until I started adding persistence to my app with HikariModule. Now Guice registry creation is a bit more complex:

    .join(Guice.registry(b -> b 
        .module(HikariModule.class, hikariConfig -> { 
            final String dbUrl = System.getenv("JDBC_DATABASE_URL"); 
            hikariConfig.setJdbcUrl(dbUrl); 
        }) 
        .module(Module.class) 
        .bind(DbMigrator.class) 
    ).apply(r)) 

Because my registry now consists of multiple modules if I have a service that depends on DataSource class coming from HikariModule guice injection fails in tests.

My goal is to allow writing tests in the following fashion:

@Inject // <- not required can be done in @Before method
private UserService userService; // <- Inject it somehow from Application under test 

@Test 
public void testUser() { 
    final Result<User, String> userResult = userService.create(new User.Registration()); 
    final ReceivedResponse res = aut.getHttpClient().get("/users/" + user.userId); 
    assertEquals(200, res.getStatusCode()); 
} 

What is the right approach of injecting service dependencies in tests? I would very much prefer reusing guice modules from MainClassApplicationUnderTest rather than creating my own and overriding them.


Solution

  • After quite some time battling with this issue and help from Ratpack slack I managed to pull this off.

    First of all we need to capture our application registry in the local variable.

    private Registry appRegistry;
    
    protected MainClassApplicationUnderTest aut = new MainClassApplicationUnderTest(App.class) {
        @Override
        protected void addImpositions(final ImpositionsSpec impositions) {
            impositions.add(UserRegistryImposition.of(r -> {
                appRegistry = r;
                return Registry.empty();
            }));
        }
    };
    

    It turns out there is a nifty method that starts the application. So when injecting the class we will know that Registry will not be null and we can inject classes.

    protected <T> T inject(final Class<T> classOf) {
        aut.getAddress();
        return appRegistry.get(classOf);
    }
    

    Then in test classes we can simply inject any class that is present in the registry.

    final UserService userService = inject(UserService.class);
    // OR
    final DataSource dataSource = inject(DataSource.class);