Testing with stubs¶
Stubs are hand-made replacements of real application services ("manual" or "lazy" mocks).
Guicey provides StubsHook
for overriding guice beans with stub implementations.
Warning
Stubs will not work for HK2 beans
There are two main cases:
- Stub class extends existing service:
class ServiceStub extends Service
- Stub implements service interface:
class ServiceStub implements IService
Stubs replace real application services (using guice overriding modules), so stub would be injected in all services instead of the real service.
For example, suppose we have a service:
public class Service {
public String foo() {
...
}
}
where method foo implements some complex logic, not required in test.
Writing stub:
public class ServiceStub extends Service {
@Override
public String foo() {
return "static value";
}
}
Using stub in test:
StubsHook hook = new StubsHook();
// register stub (stub instance managed with guice)
hook.stub(Service.class, ServiceStub.class);
TestsSupport.build(App.class)
.hooks(hook)
.runCore(injector -> {
Service service = injector.getInstance(Service.class);
// service is a stub
Assertions.assertInstanceOf(ServiceStub.class, service);
Assertions.assertEquals("static value", service.foo());
});
Info
In many cases, mockito mocks and spies could be more useful, but stubs are simpler (easier to understand, especially comparing to spies).
In the example above, stub instance is created by guice. Stub could also be registered by instance:
hook.stub(Service.class, new ServiceStub());
In this case, stub's @Inject
fields would be processed (requestInjection(stub)
would be called).
Note
Guice AOP would apply only for stubs registered with class. So stub instance could be used (instead of class) exactly to avoid additional AOP logic for service.
Stub lifecycle¶
More complex stubs may contain a test-related internal state, which must be cleared between tests.
In this case, stub could implement StubLifecycle
:
public class ServiceStub extends Service implements StubLifecycle {
int calls;
@Override
public void before() {
calls = 0;
}
@Override
public void after() {
calls = 0;
}
}
(both methods optional)
Then we can call these methods with hook:
// call before() on all stubs
hook.before();
// test staff here
// call after after test
hook.after();
Stub instance¶
Stub instance could be obtained either from injector (using overridden service as a key):
ServiceStub stub = (ServiceStub) injector.getInstance(Service.class);
or directly from hook:
ServiceStub stub = hook.getStub(Service.class);