它能够与Spring一起使用的原因是测试类通过使用
@RunWith(SpringJUnit4ClassRunner.class)
由Spring容器管理。运行程序将把所有受管对象注入测试对象中。
JerseyTest
不会以这种方式进行管理。
如果您想要,可以创建自己的运行程序,但需要了解HK2(Jersey的DI框架)的工作原理。请查看
文档。一切都围绕
ServiceLocator
展开。在独立应用中,您可能会看到以下内容来引导DI容器。
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create(null);
ServiceLocatorUtilities.bind(locator, new MyBinder());
然后获取服务,执行
Service service = locator.getService(Service.class)
在测试类中,我们不需要获得任何对服务对象的访问权限,只需使用
ServiceLocator
注入测试对象即可。
locator.inject(test)
上面的代码中,
test
是测试类的实例,它会在自定义运行器中传递给我们。以下是自定义运行器的示例实现。
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();
}
};
}
}
在Runner中,对于每个测试方法都会调用
methodInvoker
,因此我们为每个被调用的测试方法创建一个全新的对象集合。
以下是完整的测试用例。
@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();
}
}