Testing with stubs¶
Stubs are hand-made replacements of real application services ("manual" or "lazy" mocks).
Stubs declared in test class with a new @StubBean
annotation.
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:
@TestGuiceyApp(App.class)
public class Test {
@StubBean(Service.class)
ServiceStub stub;
// injecting here to show that stub replaced real service
@Inject
Service service;
@Test
public void test(){
// 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:
@StubBean(Service.class)
ServiceStub stub = new ServiceStub();
In this case, stub's @Inject
fields would be processed (requestInjection(stub)
would be called).
Note
When stub is registered with instance, stub field must be static for per-test application run
(default annotation). It may not be static for per-method application startup (with @RegisterExtension
).
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)
Such methods would be called automatically before and after of each test method.
Debug¶
When extension debug is active:
@TestGucieyApp(value = App.class, debug = true)
public class Test
All recognized stub fields would be logged:
Applied stubs (@StubBean) on StubsSimpleTest:
StubsSimpleTest.stub2 GUICE Service2 >> Service2Stub
StubsSimpleTest.stub GUICE Service1 >> Service1Stub