mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-25 11:51:15 +08:00
prepare mock association util
This commit is contained in:
parent
4c6fc5b228
commit
02c74299f4
@ -7,10 +7,9 @@ import org.objectweb.asm.tree.*;
|
||||
*/
|
||||
abstract public class BaseClassWithContextHandler extends BaseClassHandler {
|
||||
|
||||
protected static final String CLASS_TESTABLE_TOOL = "com/alibaba/testable/core/tool/TestableTool";
|
||||
protected static final String CLASS_TESTABLE_UTIL = "com/alibaba/testable/core/util/TestableUtil";
|
||||
protected static final String CLASS_MOCK_CONTEXT_UTIL = "com/alibaba/testable/core/util/MockContextUtil";
|
||||
protected static final String CLASS_INVOKE_RECORD_UTIL = "com/alibaba/testable/core/util/InvokeRecordUtil";
|
||||
private static final String CLASS_TESTABLE_TOOL = "com/alibaba/testable/core/tool/TestableTool";
|
||||
private static final String CLASS_TESTABLE_UTIL = "com/alibaba/testable/core/util/TestableUtil";
|
||||
private static final String FIELD_SOURCE_METHOD = "SOURCE_METHOD";
|
||||
private static final String FIELD_MOCK_CONTEXT = "MOCK_CONTEXT";
|
||||
private static final String METHOD_PARAMETERS = "parameters";
|
||||
|
@ -19,8 +19,13 @@ import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassNa
|
||||
*/
|
||||
public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
|
||||
private static final String CLASS_INVOKE_RECORD_UTIL = "com/alibaba/testable/core/util/InvokeRecordUtil";
|
||||
private static final String CLASS_MOCK_ASSOCIATION_UTIL = "com/alibaba/testable/core/util/MockAssociationUtil";
|
||||
private static final String METHOD_INVOKE_ORIGIN = "invokeOrigin";
|
||||
private static final String SIGNATURE_INVOKE_ORIGIN =
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
|
||||
private static final String METHOD_RECORD_MOCK_INVOKE = "recordMockInvoke";
|
||||
private static final String SIGNATURE_INVOKE_RECORDER_METHOD = "([Ljava/lang/Object;ZZ)V";
|
||||
private static final String SIGNATURE_RECORDER_METHOD_INVOKE = "([Ljava/lang/Object;ZZ)V";
|
||||
private static final String METHOD_IS_ASSOCIATED = "isAssociated";
|
||||
private static final String SIGNATURE_IS_ASSOCIATED = "()Z";
|
||||
|
||||
@ -107,14 +112,20 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
}
|
||||
LabelNode firstLine = new LabelNode(new Label());
|
||||
InsnList il = new InsnList();
|
||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_CONTEXT_UTIL, METHOD_IS_ASSOCIATED,
|
||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_ASSOCIATION_UTIL, METHOD_IS_ASSOCIATED,
|
||||
SIGNATURE_IS_ASSOCIATED, false));
|
||||
il.add(new JumpInsnNode(IFNE, firstLine));
|
||||
il.add(invokeOriginalMethod(mn));
|
||||
il.add(firstLine);
|
||||
il.add( new FrameNode(F_SAME, 0, null, 0, null));
|
||||
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
|
||||
}
|
||||
|
||||
private InsnList invokeOriginalMethod(MethodNode mn) {
|
||||
InsnList il = new InsnList();
|
||||
return il;
|
||||
}
|
||||
|
||||
private boolean isGlobalScope(MethodNode mn) {
|
||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD).equals(an.desc) ||
|
||||
@ -179,7 +190,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
}
|
||||
il.add(new InsnNode(ICONST_1));
|
||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
||||
SIGNATURE_INVOKE_RECORDER_METHOD, false));
|
||||
SIGNATURE_RECORDER_METHOD_INVOKE, false));
|
||||
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||
import com.alibaba.testable.agent.util.ClassUtil;
|
||||
import com.alibaba.testable.agent.util.DiagnoseUtil;
|
||||
import com.alibaba.testable.core.util.LogUtil;
|
||||
import com.alibaba.testable.core.util.MockContextUtil;
|
||||
import com.alibaba.testable.core.util.MockAssociationUtil;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
@ -47,7 +47,7 @@ public class MockClassParser {
|
||||
* @return found annotation or not
|
||||
*/
|
||||
public boolean isMockClass(String className) {
|
||||
return MockContextUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(className)) ||
|
||||
return MockAssociationUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(className)) ||
|
||||
hasMockMethod(className);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import com.alibaba.testable.agent.model.MethodInfo;
|
||||
import com.alibaba.testable.agent.util.*;
|
||||
import com.alibaba.testable.core.model.ClassType;
|
||||
import com.alibaba.testable.core.util.LogUtil;
|
||||
import com.alibaba.testable.core.util.MockContextUtil;
|
||||
import com.alibaba.testable.core.util.MockAssociationUtil;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
@ -182,7 +182,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
// look for MockWith annotation
|
||||
String mockClassName = parseMockWithAnnotation(cn, ClassType.TestClass);
|
||||
if (mockClassName != null) {
|
||||
MockContextUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className));
|
||||
MockAssociationUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className));
|
||||
return ClassUtil.toSlashSeparatedName(mockClassName);
|
||||
}
|
||||
// look for Mock inner class
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.alibaba.testable.core.util;
|
||||
|
||||
import com.alibaba.testable.core.model.MockContext;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
||||
|
||||
public class MockAssociationUtil {
|
||||
|
||||
/**
|
||||
* [0]Thread → [1]MockAssociationUtil → [2]MockClass
|
||||
*/
|
||||
public static final int INDEX_OF_MOCK_CLASS = 2;
|
||||
|
||||
/**
|
||||
* Mock class referred by @MockWith annotation to list of its test classes
|
||||
* MockClassName (dot-separated) → Set of associated [TestClassNames (dot-separated)]
|
||||
*/
|
||||
public static Map<String, Set<String>> mockToTests = UnnullableMap.of(new HashSet<String>());
|
||||
|
||||
/**
|
||||
* Check whether current mock method is invoked from its associated test class
|
||||
* should be invoked in mock method
|
||||
*/
|
||||
public static boolean isAssociated() {
|
||||
MockContext mockContext = MockContextUtil.context.get();
|
||||
String testClassName = (mockContext == null) ? "" : mockContext.testClassName;
|
||||
String mockClassName = Thread.currentThread().getStackTrace()[INDEX_OF_MOCK_CLASS].getClassName();
|
||||
return isAssociatedByInnerMockClass(testClassName, mockClassName) ||
|
||||
isAssociatedByOuterMockClass(testClassName, mockClassName) ||
|
||||
isAssociatedByMockWithAnnotation(testClassName, mockClassName);
|
||||
}
|
||||
|
||||
public static Object invokeOrigin(String originClass, String originMethod, Object originObj, Object... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isAssociatedByInnerMockClass(String testClassName, String mockClassName) {
|
||||
return mockClassName.equals(String.format("%s$%s", testClassName, MOCK_POSTFIX));
|
||||
}
|
||||
|
||||
private static boolean isAssociatedByOuterMockClass(String testClassName, String mockClassName) {
|
||||
return testClassName.endsWith(TEST_POSTFIX) &&
|
||||
mockClassName.equals(testClassName.substring(0, testClassName.length() - 4) + MOCK_POSTFIX);
|
||||
}
|
||||
|
||||
private static boolean isAssociatedByMockWithAnnotation(String testClassName, String mockClassName) {
|
||||
return mockToTests.get(mockClassName).contains(testClassName);
|
||||
}
|
||||
|
||||
}
|
@ -5,9 +5,6 @@ import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
||||
|
||||
public class MockContextUtil {
|
||||
|
||||
/**
|
||||
@ -16,23 +13,17 @@ public class MockContextUtil {
|
||||
public static InheritableThreadLocal<MockContext> context = new TransmittableThreadLocal<MockContext>();
|
||||
|
||||
/**
|
||||
* Mock class referred by @MockWith annotation to list of its test classes
|
||||
* MockClassName (dot-separated) → Set of associated [TestClassNames (dot-separated)]
|
||||
* [0]Thread → [1]MockContextUtil → [2]TestClass
|
||||
*/
|
||||
public static Map<String, Set<String>> mockToTests = UnnullableMap.of(new HashSet<String>());
|
||||
|
||||
/**
|
||||
* [0]Thread → [1]MockContextUtil → [2]TestClass/MockClass
|
||||
*/
|
||||
public static final int INDEX_OF_INVOKER_CLASS = 2;
|
||||
public static final int INDEX_OF_TEST_CLASS = 2;
|
||||
|
||||
/**
|
||||
* Initialize mock context
|
||||
* should be invoked at the beginning of each test case method
|
||||
*/
|
||||
public static void init() {
|
||||
String testClassName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getClassName();
|
||||
String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getMethodName();
|
||||
String testClassName = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS].getClassName();
|
||||
String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS].getMethodName();
|
||||
context.set(new MockContext(testClassName, testCaseName));
|
||||
}
|
||||
|
||||
@ -49,30 +40,4 @@ public class MockContextUtil {
|
||||
return mockContext == null ? new HashMap<String, Object>() : mockContext.parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether current mock method is invoked from its associated test class
|
||||
* should be invoked in mock method
|
||||
*/
|
||||
public static boolean isAssociated() {
|
||||
MockContext mockContext = context.get();
|
||||
String testClassName = (mockContext == null) ? "" : mockContext.testClassName;
|
||||
String mockClassName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getClassName();
|
||||
return isAssociatedByInnerMockClass(testClassName, mockClassName) ||
|
||||
isAssociatedByOuterMockClass(testClassName, mockClassName) ||
|
||||
isAssociatedByMockWithAnnotation(testClassName, mockClassName);
|
||||
}
|
||||
|
||||
private static boolean isAssociatedByInnerMockClass(String testClassName, String mockClassName) {
|
||||
return mockClassName.equals(String.format("%s$%s", testClassName, MOCK_POSTFIX));
|
||||
}
|
||||
|
||||
private static boolean isAssociatedByOuterMockClass(String testClassName, String mockClassName) {
|
||||
return testClassName.endsWith(TEST_POSTFIX) &&
|
||||
mockClassName.equals(testClassName.substring(0, testClassName.length() - 4) + MOCK_POSTFIX);
|
||||
}
|
||||
|
||||
private static boolean isAssociatedByMockWithAnnotation(String testClassName, String mockClassName) {
|
||||
return mockToTests.get(mockClassName).contains(testClassName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,21 +5,21 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MockContextUtilTest {
|
||||
class MockAssociationUtilTest {
|
||||
|
||||
@Test
|
||||
void should_able_to_associate_by_inner_mock_class() {
|
||||
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByInnerMockClass",
|
||||
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByInnerMockClass",
|
||||
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTest$Mock"));
|
||||
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByInnerMockClass",
|
||||
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByInnerMockClass",
|
||||
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTestMock"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_able_to_associate_by_outer_mock_class() {
|
||||
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByOuterMockClass",
|
||||
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByOuterMockClass",
|
||||
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoMock"));
|
||||
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByOuterMockClass",
|
||||
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByOuterMockClass",
|
||||
"com.alibaba.testable.DemoTester", "com.alibaba.testable.DemoMock"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user