2015-06-11 31 views
2

我這裏有一個接口使用新澤西州的依賴注入在一個獨立的應用

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); 
    } 
} 
依賴一類

現在說我想測試樣品類

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仍然是空的。我想這是因爲當執行到達指定了綁定的那一行時,這個測試類已經被創建了。但是我不確定。 Autowired而不是澤西CDI同樣的作品

Had Sample是一個資源/控制器類,測試框架將創建它的一個實例而不需要注入它,但有可能使用Jersey DI測試任何其他非Web類?

回答

5

它與Spring一起工作的原因是測試類由Spring容器通過使用@RunWith(SpringJUnit4ClassRunner.class)進行管理。跑步者將所有託管對象注入到測試對象中。這種方式不管理JerseyTest

如果你願意,你可以創建自己的跑步者,但你需要了解一下HK2(澤西島的DI框架)是如何工作的。看看the documentation。一切都圍繞着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被調用每個測試方法,所以我們正在創造一個全新的集每個稱爲測試方法的對象。

下面是一個完整的測試用例

@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(); 
    } 
} 
0

我面臨同樣的情況,但在運行一些測試的集成,需要有一些我的應用程序已經定義了單身的情況下。

我發現的訣竅是以下幾點。你只需要創建一個正常的測試類或獨立使用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就可以開始您喜歡的應用程序說here。 意味着您可以訪問應用程序需要啓動的所有內容和每個對象。就我而言,我需要訪問我的服務單身我這樣做,使用@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; 

} 

運行,這將設置爲null爲@注入是行不通的,因爲我們不」服務在這一點上沒有任何東西把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); 

    } 

這樣可以避免在您的應用程序之外創建其他綁定器和配置。

參考了ServiceLocatorDropwizardAppRule將創建可以發現here