我一直在一个项目中,我想动态地拦截对外部依赖项的调用并在运行时模拟它们的返回。我首先实现了一个小的概念证明,您可以在这里找到:
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)即可工作]
我一直在一个项目中,我想动态地拦截对外部依赖项的调用并在运行时模拟它们的返回。我首先实现了一个小的概念证明,...
我解决了!拦截器不能使用cdi(构造函数或字段注入),因此我最终要做的是在类上删除cdi单例注释,并用“传统”单例方法代替它,我将其创建为生产者。然后,我可以使用cdi或不使用cdi的bean。我不得不在拦截器中使用后者。