I have a interface here
interface Idemo{
public int getDemo(int i);
}
And it's one implementation
class DemoImpl implements Idemo{
@Override
public int getDemo(int i){
return i+10;
}
}
And there is a class which has a dependency on Idemo
class Sample{
@Inject
Idemo demo;
public int getSample(int i){
return demo.getDemo(i);
}
}
Now say I want to test Sample class
public class SampleTest extends JerseyTest {
@Inject
Sample s;
@Override
protected Application configure() {
AbstractBinder binder = new AbstractBinder() {
@Override
protected void configure() {
bind(Demo.class).to(Idemo.class);
bind(Sample.class).to(Sample.class); //**doesn't work**
}
};
ResourceConfig config = new ResourceConfig(Sample.class);
config.register(binder);
return config;
}
@Test
public void test_getSample() {
assertEquals(15, s.getSample(5)); //null pointer exception
}
}
Here the Sample instance is not getting created and s remains null.I suppose this is because by the time the execution reaches line where binding is specified this test class has already been created.But I am not sure.With Spring Autowired instead of jersey CDI the same works
Had Sample been a resource/controller class the test framework would create an instance of it with no need to inject it but is it possible to test any other non-web class using Jersey DI ?
The reason it works with Spring is that the test class is managed by the Spring container by using
@RunWith(SpringJUnit4ClassRunner.class)
. The runner will inject all managed objects into the test object.JerseyTest
is not managed this way.If you want, you can create your own runner, but you need to understand a bit how HK2 (Jersey's DI framework) works. Take a look at the documentation. Everything revolves around the
ServiceLocator
. In a standalone, you might see something like this to bootstrap the DI containerThen to get the service, do
In the case of the test class, we don't need to gain any access to the service object, we can simply inject the test object, using the
ServiceLocator
:Above,
test
is the test class instance that gets passed to us in our custom runner. Here is the example implementation of a custom runnerIn the runner, the
methodInvoker
is called for every test method, so we are creating a fresh new set of objects for each test method called.Here is a complete test case
I was facing the same situation but in the context of running some integrations test that needs to have some of the singletons that my application have already defined.
The trick that I found is the following. You just need to create a normal test class or a standalone that use the
DropwizardAppRule
In my case, I use
JUnit
as I was writing some integration test.The
@ClassRule
will start your application like is said here . That means you will have access to everything and every object your application needs to start. In my case, I need to get access to a singleton for my service I do that using the@Inject
annotation and the@Named
Running this will set to null the service as @Inject is not working because we don't have at this point anything that put the beans into the references. There is where this method comes handy.
That will avoid creating other binders and configurations outside of the existing on your application.
Reference to the
ServiceLocator
thatDropwizardAppRule
creates can be found here