Я работал над проектом, в котором я хотел бы динамически перехватывать вызовы внешних зависимостей и проверять их возврат во время выполнения. Я начал с реализации небольшого доказательства концепции, которое вы можете найти здесь:
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
Вот содержимое моих бобов. 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>
Любые идеи приветствуются.
PS код работает без перехвата Торс (включая CDI)