mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-10 20:30:11 +08:00
support enable diagnose log by test class
This commit is contained in:
parent
5770a702cc
commit
401b5f83d7
@ -25,7 +25,7 @@ public class PreMain {
|
|||||||
}
|
}
|
||||||
for (String a : args.split(AND)) {
|
for (String a : args.split(AND)) {
|
||||||
if (a.equals(DEBUG)) {
|
if (a.equals(DEBUG)) {
|
||||||
LogUtil.enableDebugLog();
|
LogUtil.globalDebugEnable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,5 +14,6 @@ public class ConstPool {
|
|||||||
|
|
||||||
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
||||||
|
|
||||||
|
public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith";
|
||||||
public static final String TESTABLE_MOCK = "com.alibaba.testable.core.annotation.TestableMock";
|
public static final String TESTABLE_MOCK = "com.alibaba.testable.core.annotation.TestableMock";
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
|
|
||||||
private void transformMethod(ClassNode cn, MethodNode mn, Set<MethodInfo> memberInjectMethods,
|
private void transformMethod(ClassNode cn, MethodNode mn, Set<MethodInfo> memberInjectMethods,
|
||||||
Set<MethodInfo> newOperatorInjectMethods) {
|
Set<MethodInfo> newOperatorInjectMethods) {
|
||||||
LogUtil.debug(" Received %d member mock methods, %d constructor mock methods",
|
LogUtil.debug(" Handling method %s", mn.name);
|
||||||
memberInjectMethods.size(), newOperatorInjectMethods.size());
|
|
||||||
AbstractInsnNode[] instructions = mn.instructions.toArray();
|
AbstractInsnNode[] instructions = mn.instructions.toArray();
|
||||||
List<MethodInfo> memberInjectMethodList = new ArrayList<MethodInfo>(memberInjectMethods);
|
List<MethodInfo> memberInjectMethodList = new ArrayList<MethodInfo>(memberInjectMethods);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -156,7 +155,8 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
|
|
||||||
private AbstractInsnNode[] replaceNewOps(ClassNode cn, MethodNode mn, String newOperatorInjectMethodName,
|
private AbstractInsnNode[] replaceNewOps(ClassNode cn, MethodNode mn, String newOperatorInjectMethodName,
|
||||||
AbstractInsnNode[] instructions, int start, int end) {
|
AbstractInsnNode[] instructions, int start, int end) {
|
||||||
LogUtil.debug(" Using %s mock new operation in %s", newOperatorInjectMethodName, mn.name);
|
LogUtil.debug(" Line %d, mock method %s used", getLineNum(instructions, start),
|
||||||
|
newOperatorInjectMethodName);
|
||||||
String classType = ((TypeInsnNode)instructions[start]).desc;
|
String classType = ((TypeInsnNode)instructions[start]).desc;
|
||||||
String constructorDesc = ((MethodInsnNode)instructions[end]).desc;
|
String constructorDesc = ((MethodInsnNode)instructions[end]).desc;
|
||||||
String testClassName = ClassUtil.getTestClassName(cn.name);
|
String testClassName = ClassUtil.getTestClassName(cn.name);
|
||||||
@ -170,6 +170,15 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
return mn.instructions.toArray();
|
return mn.instructions.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getLineNum(AbstractInsnNode[] instructions, int start) {
|
||||||
|
for (int i = start - 1; i >= 0; i--) {
|
||||||
|
if (instructions[i] instanceof LineNumberNode) {
|
||||||
|
return ((LineNumberNode)instructions[i]).line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private String getConstructorInjectDesc(String constructorDesc, String classType) {
|
private String getConstructorInjectDesc(String constructorDesc, String classType) {
|
||||||
return constructorDesc.substring(0, constructorDesc.length() - 1) +
|
return constructorDesc.substring(0, constructorDesc.length() - 1) +
|
||||||
ClassUtil.toByteCodeClassName(classType);
|
ClassUtil.toByteCodeClassName(classType);
|
||||||
@ -178,7 +187,7 @@ public class SourceClassHandler extends BaseClassHandler {
|
|||||||
private AbstractInsnNode[] replaceMemberCallOps(ClassNode cn, MethodNode mn, String substitutionMethod,
|
private AbstractInsnNode[] replaceMemberCallOps(ClassNode cn, MethodNode mn, String substitutionMethod,
|
||||||
AbstractInsnNode[] instructions, String ownerClass,
|
AbstractInsnNode[] instructions, String ownerClass,
|
||||||
int opcode, int start, int end) {
|
int opcode, int start, int end) {
|
||||||
LogUtil.debug(" Using %s mock method in %s", substitutionMethod, mn.name);
|
LogUtil.debug(" Line %d, mock method %s used", getLineNum(instructions, start), substitutionMethod);
|
||||||
mn.maxStack++;
|
mn.maxStack++;
|
||||||
MethodInsnNode method = (MethodInsnNode)instructions[end];
|
MethodInsnNode method = (MethodInsnNode)instructions[end];
|
||||||
String testClassName = ClassUtil.getTestClassName(cn.name);
|
String testClassName = ClassUtil.getTestClassName(cn.name);
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.alibaba.testable.agent.model;
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.AnnotationNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record parameter fetch from @MockWith annotation
|
||||||
|
*
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
public class CachedMockParameter {
|
||||||
|
|
||||||
|
private final boolean classExist;
|
||||||
|
private final AnnotationNode mockWith;
|
||||||
|
|
||||||
|
private CachedMockParameter(boolean classExist, AnnotationNode mockWith) {
|
||||||
|
this.classExist = classExist;
|
||||||
|
this.mockWith = mockWith;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CachedMockParameter notExist() {
|
||||||
|
return new CachedMockParameter(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CachedMockParameter exist() {
|
||||||
|
return new CachedMockParameter(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CachedMockParameter exist(AnnotationNode mockWith) {
|
||||||
|
return new CachedMockParameter(true, mockWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClassExist() {
|
||||||
|
return classExist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnnotationNode getMockWith() {
|
||||||
|
return mockWith;
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,14 @@ package com.alibaba.testable.agent.transformer;
|
|||||||
import com.alibaba.testable.agent.constant.ConstPool;
|
import com.alibaba.testable.agent.constant.ConstPool;
|
||||||
import com.alibaba.testable.agent.handler.SourceClassHandler;
|
import com.alibaba.testable.agent.handler.SourceClassHandler;
|
||||||
import com.alibaba.testable.agent.handler.TestClassHandler;
|
import com.alibaba.testable.agent.handler.TestClassHandler;
|
||||||
|
import com.alibaba.testable.agent.model.CachedMockParameter;
|
||||||
import com.alibaba.testable.agent.tool.ImmutablePair;
|
import com.alibaba.testable.agent.tool.ImmutablePair;
|
||||||
import com.alibaba.testable.agent.model.MethodInfo;
|
import com.alibaba.testable.agent.model.MethodInfo;
|
||||||
import com.alibaba.testable.agent.tool.ComparableWeakRef;
|
import com.alibaba.testable.agent.tool.ComparableWeakRef;
|
||||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.agent.util.ClassUtil;
|
||||||
import com.alibaba.testable.agent.util.LogUtil;
|
import com.alibaba.testable.agent.util.LogUtil;
|
||||||
|
import com.alibaba.testable.core.model.MockDiagnose;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
import org.objectweb.asm.tree.AnnotationNode;
|
import org.objectweb.asm.tree.AnnotationNode;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
@ -18,9 +20,7 @@ import java.io.IOException;
|
|||||||
import java.lang.instrument.ClassFileTransformer;
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassName;
|
import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassName;
|
||||||
|
|
||||||
@ -29,12 +29,14 @@ import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassNa
|
|||||||
*/
|
*/
|
||||||
public class TestableClassTransformer implements ClassFileTransformer {
|
public class TestableClassTransformer implements ClassFileTransformer {
|
||||||
|
|
||||||
private final Set<ComparableWeakRef<String>> loadedClassNames = ComparableWeakRef.getWeekHashSet();
|
private static final String FIELD_DIAGNOSE = "diagnose";
|
||||||
|
private final Map<ComparableWeakRef<String>, CachedMockParameter> loadedClass =
|
||||||
|
new WeakHashMap<ComparableWeakRef<String>, CachedMockParameter>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
ProtectionDomain protectionDomain, byte[] classFileBuffer) {
|
ProtectionDomain protectionDomain, byte[] classFileBuffer) {
|
||||||
if (isSystemClass(loader, className) || loadedClassNames.contains(new ComparableWeakRef<String>(className))) {
|
if (isSystemClass(loader, className) || loadedClass.containsKey(new ComparableWeakRef<String>(className))) {
|
||||||
// Ignore system class and reloaded class
|
// Ignore system class and reloaded class
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -42,15 +44,14 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
if (shouldTransformAsSourceClass(className)) {
|
if (shouldTransformAsSourceClass(className)) {
|
||||||
// it's a source class with testable enabled
|
// it's a source class with testable enabled
|
||||||
LogUtil.debug("Handling source class %s", className);
|
LogUtil.debug("Handling source class %s", className);
|
||||||
loadedClassNames.add(new ComparableWeakRef<String>(className));
|
|
||||||
List<MethodInfo> injectMethods = getTestableMockMethods(ClassUtil.getTestClassName(className));
|
List<MethodInfo> injectMethods = getTestableMockMethods(ClassUtil.getTestClassName(className));
|
||||||
return new SourceClassHandler(injectMethods).getBytes(classFileBuffer);
|
return new SourceClassHandler(injectMethods).getBytes(classFileBuffer);
|
||||||
} else if (shouldTransformAsTestClass(className)) {
|
} else if (shouldTransformAsTestClass(className)) {
|
||||||
// it's a test class with testable enabled
|
// it's a test class with testable enabled
|
||||||
LogUtil.debug("Handling test class %s", className);
|
LogUtil.debug("Handling test class %s", className);
|
||||||
loadedClassNames.add(new ComparableWeakRef<String>(className));
|
|
||||||
return new TestClassHandler().getBytes(classFileBuffer);
|
return new TestClassHandler().getBytes(classFileBuffer);
|
||||||
}
|
}
|
||||||
|
resetMockContext();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -58,12 +59,11 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldTransformAsSourceClass(String className) {
|
private boolean shouldTransformAsSourceClass(String className) {
|
||||||
return ClassUtil.anyMethodHasAnnotation(ClassUtil.getTestClassName(className), ConstPool.TESTABLE_MOCK);
|
return hasMockAnnotation(ClassUtil.getTestClassName(className));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldTransformAsTestClass(String className) {
|
private boolean shouldTransformAsTestClass(String className) {
|
||||||
return className.endsWith(ConstPool.TEST_POSTFIX) &&
|
return className.endsWith(ConstPool.TEST_POSTFIX) && hasMockAnnotation(className);
|
||||||
ClassUtil.anyMethodHasAnnotation(className, ConstPool.TESTABLE_MOCK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSystemClass(ClassLoader loader, String className) {
|
private boolean isSystemClass(ClassLoader loader, String className) {
|
||||||
@ -107,6 +107,60 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether any method in specified class has specified annotation
|
||||||
|
* @param className class that need to explore
|
||||||
|
* @return found annotation or not
|
||||||
|
*/
|
||||||
|
private boolean hasMockAnnotation(String className) {
|
||||||
|
CachedMockParameter cache = loadedClass.get(new ComparableWeakRef<String>(className));
|
||||||
|
if (cache != null) {
|
||||||
|
setupMockContext(cache.getMockWith());
|
||||||
|
return cache.isClassExist();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ClassNode cn = new ClassNode();
|
||||||
|
new ClassReader(className).accept(cn, 0);
|
||||||
|
if (cn.visibleAnnotations != null) {
|
||||||
|
for (AnnotationNode an : cn.visibleAnnotations) {
|
||||||
|
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.MOCK_WITH)) {
|
||||||
|
setupMockContext(an);
|
||||||
|
loadedClass.put(new ComparableWeakRef<String>(className), CachedMockParameter.exist(an));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (MethodNode mn : cn.methods) {
|
||||||
|
if (mn.visibleAnnotations != null) {
|
||||||
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
|
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.TESTABLE_MOCK)) {
|
||||||
|
loadedClass.put(new ComparableWeakRef<String>(className), CachedMockParameter.exist());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Usually class not found, return without record
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
loadedClass.put(new ComparableWeakRef<String>(className), CachedMockParameter.notExist());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupMockContext(AnnotationNode an) {
|
||||||
|
MockDiagnose mockDebug = AnnotationUtil.getAnnotationParameter(an, FIELD_DIAGNOSE, null, MockDiagnose.class);
|
||||||
|
if (MockDiagnose.ENABLE.equals(mockDebug)) {
|
||||||
|
LogUtil.enableDebugLog();
|
||||||
|
} else if (MockDiagnose.DISABLE.equals(mockDebug)) {
|
||||||
|
LogUtil.disableDebugLog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetMockContext() {
|
||||||
|
LogUtil.resetDebugLog();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split desc to "first parameter" and "desc of rest parameters"
|
* Split desc to "first parameter" and "desc of rest parameters"
|
||||||
* @param desc method desc
|
* @param desc method desc
|
||||||
|
@ -17,9 +17,18 @@ public class AnnotationUtil {
|
|||||||
* @return value of parameter
|
* @return value of parameter
|
||||||
*/
|
*/
|
||||||
public static <T> T getAnnotationParameter(AnnotationNode an, String key, T defaultValue, Class<T> clazz) {
|
public static <T> T getAnnotationParameter(AnnotationNode an, String key, T defaultValue, Class<T> clazz) {
|
||||||
if (an.values != null) {
|
if (an != null && an.values != null) {
|
||||||
for (int i = 0; i < an.values.size(); i += 2) {
|
for (int i = 0; i < an.values.size(); i += 2) {
|
||||||
if (an.values.get(i).equals(key)) {
|
if (an.values.get(i).equals(key)) {
|
||||||
|
if (clazz.isEnum()) {
|
||||||
|
// Enum type are stored as String[] in annotation parameter
|
||||||
|
String[] values = (String[])an.values.get(i + 1);
|
||||||
|
if (values == null || values.length != 2) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
Class<? extends Enum> enumClazz = (Class<? extends Enum>)clazz;
|
||||||
|
return (T)Enum.valueOf(enumClazz, values[1]);
|
||||||
|
}
|
||||||
return clazz.cast(an.values.get(i + 1));
|
return clazz.cast(an.values.get(i + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,7 @@ public class ClassUtil {
|
|||||||
private static final String EMPTY = "";
|
private static final String EMPTY = "";
|
||||||
private static final String METHOD_VALUE_OF = "valueOf";
|
private static final String METHOD_VALUE_OF = "valueOf";
|
||||||
|
|
||||||
private final static String JOINER = "::";
|
|
||||||
|
|
||||||
private static final Map<Byte, String> TYPE_MAPPING = new HashMap<Byte, String>();
|
private static final Map<Byte, String> TYPE_MAPPING = new HashMap<Byte, String>();
|
||||||
private static final Map<ComparableWeakRef<String>, Boolean> loadedClass =
|
|
||||||
new WeakHashMap<ComparableWeakRef<String>, Boolean>();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
TYPE_MAPPING.put(TYPE_BYTE, CLASS_BYTE);
|
TYPE_MAPPING.put(TYPE_BYTE, CLASS_BYTE);
|
||||||
@ -61,38 +57,6 @@ public class ClassUtil {
|
|||||||
TYPE_MAPPING.put(TYPE_VOID, EMPTY);
|
TYPE_MAPPING.put(TYPE_VOID, EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether any method in specified class has specified annotation
|
|
||||||
* @param className class that need to explore
|
|
||||||
* @param annotationName annotation to look for
|
|
||||||
* @return found annotation or not
|
|
||||||
*/
|
|
||||||
public static boolean anyMethodHasAnnotation(String className, String annotationName) {
|
|
||||||
String cacheKey = className + JOINER + annotationName;
|
|
||||||
Boolean found = loadedClass.get(new ComparableWeakRef<String>(cacheKey));
|
|
||||||
if (found != null) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
ClassNode cn = new ClassNode();
|
|
||||||
new ClassReader(className).accept(cn, 0);
|
|
||||||
for (MethodNode mn : cn.methods) {
|
|
||||||
if (mn.visibleAnnotations != null) {
|
|
||||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
|
||||||
if (toDotSeparateFullClassName(an.desc).equals(annotationName)) {
|
|
||||||
loadedClass.put(new ComparableWeakRef<String>(cacheKey), true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
loadedClass.put(new ComparableWeakRef<String>(cacheKey), false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fit kotlin companion class name to original name
|
* fit kotlin companion class name to original name
|
||||||
* @param name a class name (which could be a companion class)
|
* @param name a class name (which could be a companion class)
|
||||||
|
@ -5,17 +5,31 @@ package com.alibaba.testable.agent.util;
|
|||||||
*/
|
*/
|
||||||
public class LogUtil {
|
public class LogUtil {
|
||||||
|
|
||||||
private static final int LEVEL_INFO = 0;
|
private static final int LEVEL_ERROR = 0;
|
||||||
|
private static final int LEVEL_WARN = 1;
|
||||||
private static final int LEVEL_DEBUG = 2;
|
private static final int LEVEL_DEBUG = 2;
|
||||||
private static int level = LEVEL_INFO;
|
private static final ThreadLocal<Integer> LEVEL = new ThreadLocal<Integer>();
|
||||||
|
|
||||||
|
public static boolean globalDebugEnable = false;
|
||||||
|
|
||||||
public static void debug(String msg, Object... args) {
|
public static void debug(String msg, Object... args) {
|
||||||
if (level >= LEVEL_DEBUG) {
|
if (LEVEL.get() >= LEVEL_DEBUG) {
|
||||||
System.err.println(String.format("[DEBUG] " + msg, args));
|
System.err.println(String.format("[DEBUG] " + msg, args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void enableDebugLog() {
|
public static void enableDebugLog() {
|
||||||
level = LEVEL_DEBUG;
|
LEVEL.remove();
|
||||||
|
LEVEL.set(LEVEL_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void disableDebugLog() {
|
||||||
|
LEVEL.remove();
|
||||||
|
LEVEL.set(LEVEL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetDebugLog() {
|
||||||
|
LEVEL.set(globalDebugEnable ? LEVEL_DEBUG : LEVEL_WARN);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,10 @@ package com.alibaba.testable.agent.util;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
class ClassUtilTest {
|
class ClassUtilTest {
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_able_to_get_annotation() {
|
|
||||||
assertFalse(ClassUtil.anyMethodHasAnnotation("class.not.exist", ""));
|
|
||||||
assertFalse(ClassUtil.anyMethodHasAnnotation("com.alibaba.testable.agent.util.ClassUtilTest", "annotation.not.exist"));
|
|
||||||
assertTrue(ClassUtil.anyMethodHasAnnotation("com.alibaba.testable.agent.util.ClassUtilTest", "org.junit.jupiter.api.Test"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_get_parameter_count() {
|
void should_able_to_get_parameter_count() {
|
||||||
assertEquals(0, ClassUtil.getParameterTypes("()V").size());
|
assertEquals(0, ClassUtil.getParameterTypes("()V").size());
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.alibaba.testable.core.annotation;
|
||||||
|
|
||||||
|
import com.alibaba.testable.core.model.MockDiagnose;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set extra mock parameter to test class
|
||||||
|
*
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Documented
|
||||||
|
public @interface MockWith {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* switch of mock diagnose information of current test class
|
||||||
|
* @return enable or disable
|
||||||
|
*/
|
||||||
|
MockDiagnose diagnose() default MockDiagnose.WARN_ONLY;
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,7 @@ public @interface TestableMock {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* mock specified method instead of method with same name
|
* mock specified method instead of method with same name
|
||||||
|
* @return target method name
|
||||||
*/
|
*/
|
||||||
String targetMethod() default "";
|
String targetMethod() default "";
|
||||||
|
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.alibaba.testable.core.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum MockDiagnose {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Be quiet
|
||||||
|
*/
|
||||||
|
DISABLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only show warning message
|
||||||
|
*/
|
||||||
|
WARN_ONLY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print detail diagnose logs
|
||||||
|
*/
|
||||||
|
ENABLE
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user