簡體   English   中英

如何對Jersey + Guice進行單元測試:ServiceLocator?

[英]How to unit test Jersey + Guice: ServiceLocator?

通過使用HK2的guice-bridge我設法將Jersey 2.x與Guice 3.x集成在一起。

public class MyApp extends ResourceConfig {

    @Inject
    public MyApp(ServiceLocator serviceLocator) {
        packages("com.mycompany");

        ...

        GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
        GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
        guiceBridge.bridgeGuiceInjector(GuiceContext.INJECTOR);
    }
}

但現在我的澤西島測試不再適用了。

public abstract class AbstractJerseyTest extends JerseyTest {

    public AbstractJerseyTest() throws TestContainerException {
        super(new InMemoryTestContainerFactory());
    }

    protected Application configure() {
        new MyApp(); // ERROR: missing 'ServiceLocator'
    }

}

那么我在哪里可以獲得用於單元測試的ServiceLocator

也許更簡潔的方法是簡單地使用Feature並在那里配置橋而不是在ResourceConfig

public class GuiceFeature implements Feature {

    public void configure(FeatureContext context) {
        ServiceLocator serviceLocator = ServiceLocatorProvider.getServiceLocator(context);
        GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
        GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
        guiceBridge.bridgeGuiceInjector(GuiceContext.INJECTOR);
    }
}

這可以像任何其他功能一樣注冊。 只需register它或使用@Provider掃描它。

請注意, ServiceLocatorProvider僅適用於Jersey 2.6及更高版本。

這是一個有效的解決方案。

關鍵是覆蓋JerseyTest的configureDeployment()方法並通過傳遞特定於Application的ResourceConfig.class而不是重寫configure()方法並返回ResourceConfig實例來創建DeploymentContext,以便測試容器正確初始化guice-bridge。

這是以下版本的Jersey,Guice和HK2 guice-bridge

    <jersey.version>2.15</jersey.version>
    <jackson2.version>2.4.4</jackson2.version>
    <hk2.guice.bridge.version>2.4.0-b10</hk2.guice.bridge.version>
    <guice.version>4.0-beta5</guice.version>

1)我的服務類

public interface MyService {
    public void hello();
}

2)我的模擬服務Impl

public class MyMockServiceImpl implements MyService{
    public void hello() {
        System.out.println("Hi");
    }
} 

3)我的資源類與Guice注入服務

@Path("myapp")
public class MyResource {

    private final MyService myService;

    @Inject
    public MyResource(MyService myService) {
        this.myService = myService;
    }
}

4)我的資源測試課程

public class MyResourceTest extends JerseyTestNg.ContainerPerClassTest {

    @Override
    protected Application configure() {
        return null;
    }

    @Override
    protected DeploymentContext configureDeployment() {
        return DeploymentContext.builder(MyTestConfig.class).build();
    }

    // other test and setup/teardown methods
}

5)ResourceConfig類

static class MyTestConfig extends ResourceConfig {

    @Inject
    public MyTestConfig(ServiceLocator serviceLocator) {

        packages("com.myapp.rest");

        GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);

        GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
        guiceBridge.bridgeGuiceInjector(Guice.createInjector(new MyTestModule()));

    }
}

6)我的Guice測試模塊課程

public class MyTestModule implements Module {

    @Override
    public void configure(Binder binder) {
            binder.bind(MyService.class)
               .to(MyMockServiceImpl.class);
    }

}

注意:我之前從未使用過澤西島。 但是,您不應該再調用new MyApp() ; 否則Guice將無效。 相反,我可能嘗試這樣的事情:

public abstract class AbstractJerseyTest extends JerseyTest {
    private final Module[] modules;

    public AbstractJerseyTest(Module... modules) throws TestContainerException {
        super(new InMemoryTestContainerFactory());
        this.module = modules;
    }

    protected Application configure() {
        Injector inj = Guice.createInjector(modules);
        return inj.getInstance(MyApp.class);
    }
}

public class ActualTest extends AbstractJerseyTest {
    private static class TestModule extends AbstractModule {
        @Override
        public void configure() {
            // Do your guice bindings here
        }
    }

    public ActualTest() throws TestContainerException {
        super(new TestModule());
    }
}

我們在Groovy的幫助下工作:

public class MemoryTestContainerFactory implements TestContainerFactory {

    private final Class<? extends Application> jaxrsApplicationClass;

    public MemoryTestContainerFactory(Class<? extends Application> jaxrsApplicationClass) {
        this.jaxrsApplicationClass = jaxrsApplicationClass;
    }

    @Override
    public TestContainer create(URI baseUri, DeploymentContext context) throws IllegalArgumentException {
        return new MemoryTestContainer(jaxrsApplicationClass, baseUri, context);
    }

    private static class MemoryTestContainer implements TestContainer {

        private final URI baseUri;
        private final ApplicationHandler appHandler;
        private final AtomicBoolean started = new AtomicBoolean(false);

        private static final Logger LOGGER = Logger.getLogger(MemoryTestContainer.class.getName());

        MemoryTestContainer(Class<? extends Application> jaxrsApplicationClass, URI baseUri, DeploymentContext context) {
            this.baseUri = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();    
            this.appHandler = new ApplicationHandler(jaxrsApplicationClass);
        }

        @Override
        public ClientConfig getClientConfig() {
            def provider = new InMemoryConnector.Provider(baseUri, appHandler) // private access (only works with Groovy)
            return new ClientConfig().connectorProvider(provider);
        }

        @Override
        public URI getBaseUri() {
            return baseUri;
        }

        @Override
        public void start() {
            if (started.compareAndSet(false, true)) {
                LOGGER.log(Level.FINE, "Starting InMemoryContainer...");
            } else {
                LOGGER.log(Level.WARNING, "Ignoring start request - InMemoryTestContainer is already started.");
            }
        }

        @Override
        public void stop() {
            if (started.compareAndSet(true, false)) {
                LOGGER.log(Level.FINE, "Stopping InMemoryContainer...");
            } else {
                LOGGER.log(Level.WARNING, "Ignoring stop request - InMemoryTestContainer is already stopped.");
            }
        }
    }
}

它不漂亮,但它的工作原理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM