问题描述
我这里有一个界面
interface Idemo{
public int getDemo(int i);
}
这是一种实现
class DemoImpl implements Idemo{
@Override
public int getDemo(int i){
return i+10;
}
}
还有一个类依赖于 Idemo
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);
}
}
现在说我要测试 Sample 类
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
}
}
这里 Sample 实例没有被创建,s 保持为空.我想这是因为当执行到达指定绑定的行时,这个测试类已经创建.但我不确定.用 Spring Autowired 代替球衣 CDI 相同的作品
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
如果 Sample 是一个资源/控制器类,测试框架将创建它的一个实例而无需注入它,但是否可以使用 Jersey DI 测试任何其他非 Web 类?
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 ?
推荐答案
之所以能与 Spring 一起工作,是因为测试类是由 Spring 容器使用 @RunWith(SpringJUnit4ClassRunner.class)
.运行器会将所有托管对象注入到测试对象中.JerseyTest
不是这样管理的.
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.
如果您愿意,您可以创建自己的跑步者,但您需要了解一点 HK2(Jersey 的 DI 框架)的工作原理.查看文档.一切都围绕着ServiceLocator
.在独立环境中,您可能会看到类似这样的内容来引导 DI 容器
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 container
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create(null);
ServiceLocatorUtilities.bind(locator, new MyBinder());
然后为了得到服务,做
Service service = locator.getService(Service.class);
在测试类的情况下,我们不需要获得对服务对象的任何访问权限,我们可以简单地使用 ServiceLocator
注入测试对象:
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
:
locator.inject(test);
上面,test
是在我们的自定义运行器中传递给我们的测试类实例.这是自定义运行器的示例实现
Above, test
is the test class instance that gets passed to us in our custom runner. Here is the example implementation of a custom runner
import java.lang.annotation.*;
import org.glassfish.hk2.api.*;
import org.glassfish.hk2.utilities.*;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.*;
public class Hk2ClassRunner extends BlockJUnit4ClassRunner {
private final ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
private Class<? extends Binder>[] binderClasses;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public static @interface Binders {
public Class<? extends Binder>[] value();
}
public Hk2ClassRunner(Class<?> cls) throws InitializationError {
super(cls);
Binders bindersAnno = cls.getClass().getAnnotation(Binders.class);
if (bindersAnno == null) {
binderClasses = new Class[0];
}
}
@Override
public Statement methodInvoker(FrameworkMethod method, final Object test) {
final Statement statement = super.methodInvoker(method, test);
return new Statement() {
@Override
public void evaluate() throws Throwable {
ServiceLocator locator = factory.create(null);
for (Class<? extends Binder> c : binderClasses) {
try {
ServiceLocatorUtilities.bind(locator, c.newInstance());
} catch (InstantiationException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
locator.inject(test);
statement.evaluate();
locator.shutdown();
}
};
}
}
在运行器中,methodInvoker
为每个测试方法调用,因此我们为每个调用的测试方法创建一组全新的对象.
In 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
@Binders({ServiceBinder.class})
@RunWith(Hk2ClassRunner.class)
public class InjectTest {
public static class Service {
@Inject
private Demo demo;
public void doSomething() {
System.out.println("Inside Service.doSomething()");
demo.doSomething();
}
}
public static class Demo {
public void doSomething() {
System.out.println("Inside Demo.doSomething()");
}
}
public static class ServiceBinder extends AbstractBinder {
@Override
protected void configure() {
bind(Demo.class).to(Demo.class);
bind(Service.class).to(Service.class);
}
}
@Inject
private Service service;
@Test
public void testInjections() {
Assert.assertNotNull(service);
service.doSomething();
}
}
这篇关于在独立应用程序中使用 Jersey 的依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!