我一直在研究一个项目,在这里我想动态地拦截对外部依赖项的调用并在运行时模拟它们的返回。我首先实现了一个小的概念证明,您可以在这里找到:
https://gitlab.com/connorbutch/mock-manager
本质上,该项目的作用是:
但是,我遇到的错误是:
Exception in thread "main" javax.enterprise.inject.CreationException
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at java.base/java.lang.Class.newInstance(Class.java:584)
at org.jboss.weld.security.NewInstanceAction.run(NewInstanceAction.java:33)
//way more logs here
这是我的beans.xml的内容:
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all" version="2.0">
<interceptors>
<class>com.connor.mock.IntegrationTestInterceptor</class>
</interceptors>
这是拦截器类:
package com.connor.mock;
import java.lang.reflect.Method;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import com.connor.ws.ExternalDependency;
/**
* This class holds the interceptors
* @author connor
*
*/
@Interceptor
@MockForIntegrationTest
public class IntegrationTestInterceptor {
private final MockManagerExternalDependencyImpl mockManager;
/**
* Cdi-enabled constructor
* @param mockManager
*/
@Inject
public IntegrationTestInterceptor(MockManagerExternalDependencyImpl mockManager) {
this.mockManager = mockManager;
}
/**
* This method intercepts external calls for integration tests, and passes them to the mock
* @param context
* @throws Exception
*/
@AroundInvoke
public Object interceptAndMockExternalCall(InvocationContext context) throws Exception {
System.out.println("Inside InterceptorHolder.interceptAndMockExternalCall");
//get what we can from the
Class<?> clazz = context.getTarget().getClass();
String methodName = context.getMethod().getName();
Object[] argsForMethod = context.getParameters();
//loop through parameters and create an array of their types for use with reflection later
Class<?>[] parameterTypes = new Class<?>[argsForMethod.length];
for(int i = 0; i < argsForMethod.length; ++i) {
parameterTypes[i] = argsForMethod[i].getClass();
}
//this means that this annotation was used on an incorrect method/class that does not implement our marker interface
if(!ExternalDependency.class.isAssignableFrom(clazz)) {
throw new Exception("Please only annotate methods with mockforintegration test interceptor binding if the class implements the marker interface ApiClient");
}
//we know this is assignable, so we can cast here
//get our mock from the singleton mock manager
@SuppressWarnings("unchecked") //we know this is safe to cast because of the assignable from check above
Object mockToInvoke = mockManager.getMockedInstance((Class<? extends ExternalDependency>) clazz);
//get the method to invoke on the mock from the method name and parameters
Method methodToInvoke = mockToInvoke.getClass().getMethod(methodName, parameterTypes);
Object returnValue;
try {
returnValue = methodToInvoke.invoke(mockToInvoke, argsForMethod);
}catch(Exception e) {
//TODO
//NOTE: break out into separate catch blocks for each type -- original method throws exception, then rethrow
throw new Exception("There was an error using reflection to invoke method on mock", e);
}
//log success here
System.out.println(returnValue);
return returnValue;
}
}
下面是使用拦截器的类:
package com.connor.ws;
import com.connor.mock.MockForIntegrationTest;
public class ExternalDependencyPolicyClientImpl implements ExternalDependency {
/**
*
*/
private static final long serialVersionUID = -8845199637628066567L;
/**
* This is a dummy method
* @return
*/
@MockForIntegrationTest
public String dummy() {
return "dummy";
}
}
这是我pom的副本:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.connor</groupId>
<artifactId>mock-manager</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>This project demonstrates how to use mock manager for integration tests involving outside web services.</description>
<properties>
<!-- dependency versions, please keep in alphabetical order -->
<cdi.version>2.0.SP1</cdi.version>
<inject.version>1</inject.version>
<interceptor.version>1.2.2</interceptor.version>
<mockito.version>3.1.0</mockito.version>
<weld.version>3.1.3.Final</weld.version>
<!-- use java 8: TODO make use java 14 -->
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>${cdi.version}</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>${inject.version}</version>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>${interceptor.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>${weld.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
</dependency>
</dependencies>
任何想法都将受到欢迎。
P.S。代码可以在没有拦截器(包括cdi)的情况下工作
答案 0 :(得分:0)
我解决了!拦截器不能使用cdi(构造函数或字段注入),因此我最终要做的是在类上删除cdi单例注释,并用“传统”单例方法代替它,我将其制成了生产者。然后,我可以使用cdi或不使用cdi的bean。我必须在拦截器中使用后者。