如何使用 JerseyTest 模拟依赖于限定符的注入值
How to mock Injected value relying on a qualifier using JerseyTest
我正在尝试模拟 controller/resource 包括 jax-rs 层。 class 具有需要注入的依赖项。
然而,它也有一些使用限定符接口注入的 String
值。
基本上,我使用 JerseyTest 运行 单个控制器并使用 HK2 进行依赖注入。我创建了一个 ResourceConfig
并注册了一个 AbstractBinder
来绑定注入的 classes。
这对于常规注入的依赖项工作正常,但是当添加额外的 @SomeQualifierInterface
注释时,它会崩溃并出现以下错误:
MultiException stack 1 of 3
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=String,parent=ThingsController,qualifiers={@com.company.SomeQualifierInterface()},position=-1,optional=false,self=false,unqualified=null,10035302)
...
MultiException stack 2 of 3
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.company.ThingsController errors were found
...
MultiException stack 3 of 3
java.lang.IllegalStateException: Unable to perform operation: resolve on com.company.ThingsController
...
请参阅下面的简化完整代码示例:
控制器/资源
import org.slf4j.Logger;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
@Path("/things")
public class ThingsController {
@Inject
private Logger log;
@Inject
@SomeQualifierInterface
private String injectedQualifierValue;
@GET
public Response getThings() {
log.info("getting things");
System.out.println("Injected value: " + injectedQualifierValue);
return Response.status(200).entity("hello world!").build();
}
}
限定符接口
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, METHOD, FIELD, PARAMETER })
public @interface SomeQualifierInterface { }
制作服务
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
@ApplicationScoped
public class SomeProducerService {
@Produces
@Dependent
@SomeQualifierInterface
public String getQualifierValue() {
return "some value!";
}
}
测试
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.slf4j.Logger;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class MockedThingsControllerTest extends JerseyTest {
private Logger logMock = mock(Logger.class);
@Override
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(ThingsController.class);
resourceConfig.register(new AbstractBinder() {
@Override
protected void configure() {
bind(logMock).to(Logger.class);
bind("some mocked value").to(String.class); // Doesn't work
bind(new SomeProducerService()).to(SomeProducerService.class); // Doesn't work
}
});
return resourceConfig;
}
@Test
public void doSomething() {
Response response = target("/things").request().get();
assertEquals(200, response.getStatus());
verify(logMock).info("getting things");
}
}
POM
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.27.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
已解决!
首先,使用 org.glassfish.hk2.utilities.binding.AbstractBinder
中的 AbstractBinder
而不是 org.glassfish.jersey.internal.inject.AbstractBinder
。
其次,创建一个扩展 AnnotationLiteral
并实现接口的 class。
最后,将值绑定到一个 TypeLiteral
并将 qualifiedBy 设置为 AnnotationLiteral。
完整代码:
import org.glassfish.hk2.api.AnnotationLiteral;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.slf4j.Logger;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class MockedThingsControllerTest extends JerseyTest {
private Logger logMock = mock(Logger.class);
@Override
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(ThingsController.class);
resourceConfig.register(new AbstractBinder() {
@Override
protected void configure() {
bind(logMock).to(Logger.class);
bind("some mocked value").to(new TypeLiteral<String>() {}).qualifiedBy(new SomeQualifierLiteral());
}
});
return resourceConfig;
}
@Test
public void doSomething() {
Response response = target("/things").request().get();
assertEquals(200, response.getStatus());
verify(logMock).info("getting things");
}
static class SomeQualifierLiteral extends AnnotationLiteral<SomeQualifierInterface> implements SomeQualifierInterface {}
}
我正在尝试模拟 controller/resource 包括 jax-rs 层。 class 具有需要注入的依赖项。
然而,它也有一些使用限定符接口注入的 String
值。
基本上,我使用 JerseyTest 运行 单个控制器并使用 HK2 进行依赖注入。我创建了一个 ResourceConfig
并注册了一个 AbstractBinder
来绑定注入的 classes。
这对于常规注入的依赖项工作正常,但是当添加额外的 @SomeQualifierInterface
注释时,它会崩溃并出现以下错误:
MultiException stack 1 of 3
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=String,parent=ThingsController,qualifiers={@com.company.SomeQualifierInterface()},position=-1,optional=false,self=false,unqualified=null,10035302)
...
MultiException stack 2 of 3
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.company.ThingsController errors were found
...
MultiException stack 3 of 3
java.lang.IllegalStateException: Unable to perform operation: resolve on com.company.ThingsController
...
请参阅下面的简化完整代码示例:
控制器/资源
import org.slf4j.Logger;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
@Path("/things")
public class ThingsController {
@Inject
private Logger log;
@Inject
@SomeQualifierInterface
private String injectedQualifierValue;
@GET
public Response getThings() {
log.info("getting things");
System.out.println("Injected value: " + injectedQualifierValue);
return Response.status(200).entity("hello world!").build();
}
}
限定符接口
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, METHOD, FIELD, PARAMETER })
public @interface SomeQualifierInterface { }
制作服务
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
@ApplicationScoped
public class SomeProducerService {
@Produces
@Dependent
@SomeQualifierInterface
public String getQualifierValue() {
return "some value!";
}
}
测试
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.slf4j.Logger;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class MockedThingsControllerTest extends JerseyTest {
private Logger logMock = mock(Logger.class);
@Override
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(ThingsController.class);
resourceConfig.register(new AbstractBinder() {
@Override
protected void configure() {
bind(logMock).to(Logger.class);
bind("some mocked value").to(String.class); // Doesn't work
bind(new SomeProducerService()).to(SomeProducerService.class); // Doesn't work
}
});
return resourceConfig;
}
@Test
public void doSomething() {
Response response = target("/things").request().get();
assertEquals(200, response.getStatus());
verify(logMock).info("getting things");
}
}
POM
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.27.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
已解决!
首先,使用 org.glassfish.hk2.utilities.binding.AbstractBinder
中的 AbstractBinder
而不是 org.glassfish.jersey.internal.inject.AbstractBinder
。
其次,创建一个扩展 AnnotationLiteral
并实现接口的 class。
最后,将值绑定到一个 TypeLiteral
并将 qualifiedBy 设置为 AnnotationLiteral。
完整代码:
import org.glassfish.hk2.api.AnnotationLiteral;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.slf4j.Logger;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class MockedThingsControllerTest extends JerseyTest {
private Logger logMock = mock(Logger.class);
@Override
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(ThingsController.class);
resourceConfig.register(new AbstractBinder() {
@Override
protected void configure() {
bind(logMock).to(Logger.class);
bind("some mocked value").to(new TypeLiteral<String>() {}).qualifiedBy(new SomeQualifierLiteral());
}
});
return resourceConfig;
}
@Test
public void doSomething() {
Response response = target("/things").request().get();
assertEquals(200, response.getStatus());
verify(logMock).info("getting things");
}
static class SomeQualifierLiteral extends AnnotationLiteral<SomeQualifierInterface> implements SomeQualifierInterface {}
}