[英]Using Jersey's Dependency Injection in a Standalone application
我在這里有一個界面
interface Idemo{
public int getDemo(int i);
}
這是一種實現
class DemoImpl implements Idemo{
@Override
public int getDemo(int i){
return i+10;
}
}
並且有一類依賴於Idemo
class Sample{
@Inject
Idemo demo;
public int getSample(int i){
return demo.getDemo(i);
}
}
現在說我要測試Sample類
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而不是jersey CDI相同的作品
如果Sample是資源/控制器類,則測試框架可以創建它的實例而無需注入它,但是是否可以使用Jersey DI測試其他任何非Web類呢?
它與Spring配合使用的原因是測試類由Spring容器通過使用@RunWith(SpringJUnit4ClassRunner.class)
。 運行程序會將所有托管對象注入測試對象。 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();
}
};
}
}
在methodInvoker
程序中,每個測試方法都會調用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();
}
}
我面臨着同樣的情況,但是在運行一些集成測試時,它需要具有我的應用程序已經定義的一些單例。
我發現的技巧如下。 您只需要創建一個普通的測試類或使用DropwizardAppRule
的獨立類DropwizardAppRule
就我而言,我在編寫集成測試時使用了JUnit
。
public class MyIntegrationTest{
//CONFIG_PATH is just a string that reference to your yaml.file
@ClassRule
public static final DropwizardAppRule<XXXConfiguration> APP_RULE =
new DropwizardAppRule<>(XXXApplication.class, CONFIG_PATH);
}
@ClassRule
將像在這里所說的那樣啟動您的應用程序。 這意味着您將有權訪問應用程序需要啟動的所有內容和每個對象。 就我而言,我需要使用我的服務訪問一個單例,使用@Inject
批注和@Named
public class MyIntegrationTest {
@ClassRule
public static final DropwizardAppRule<XXXConfiguration> APP_RULE =
new DropwizardAppRule<>(XXXAplication.class, CONFIG_PATH);
@Inject
@Named("myService")
private ServiceImpl myService;
}
運行此命令將使服務無效,因為@Inject無法正常工作,因為此時我們沒有任何將Bean放入引用中的內容。 在此方法很方便。
@Before
public void setup() {
ServiceLocator serviceLocator =((ServletContainer)APP_RULE.getEnvironment().getJerseyServletContainer()).getApplicationHandler().getServiceLocator();
//This line will take the beans from the locator and inject them in their
//reference, so each @Inject reference will be populated.
serviceLocator.inject(this);
}
這樣可以避免在應用程序現有目錄之外創建其他綁定程序和配置。
可以在此處找到對DropwizardAppRule
創建的ServiceLocator
引用
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.