mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-10 12:20:09 +08:00
use mock context to fetch current test case
This commit is contained in:
parent
94c5aa8621
commit
9a22f361f3
@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
@ -108,14 +108,13 @@ class DemoMockTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_able_to_get_test_case_name() throws Exception {
|
void should_able_to_set_mock_context() throws Exception {
|
||||||
MOCK_CONTEXT.put("case", "special_case");
|
MOCK_CONTEXT.put("case", "special_case");
|
||||||
// synchronous
|
// synchronous
|
||||||
assertEquals("mock_special", demoMock.callerOne());
|
assertEquals("mock_special", demoMock.callerOne());
|
||||||
// asynchronous
|
// asynchronous
|
||||||
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
|
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
|
||||||
verify("callFromDifferentMethod").withTimes(2);
|
verify("callFromDifferentMethod").withTimes(2);
|
||||||
MOCK_CONTEXT.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,11 @@ public class PreMain {
|
|||||||
private static final String EQUAL = "=";
|
private static final String EQUAL = "=";
|
||||||
|
|
||||||
public static void premain(String agentArgs, Instrumentation inst) {
|
public static void premain(String agentArgs, Instrumentation inst) {
|
||||||
|
// add transmittable thread local transformer
|
||||||
|
TtlAgent.premain(agentArgs, inst);
|
||||||
|
// add testable mock transformer
|
||||||
parseArgs(agentArgs);
|
parseArgs(agentArgs);
|
||||||
inst.addTransformer(new TestableClassTransformer());
|
inst.addTransformer(new TestableClassTransformer());
|
||||||
// add TTL Transformer
|
|
||||||
TtlAgent.premain(agentArgs, inst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void parseArgs(String args) {
|
private static void parseArgs(String args) {
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package com.alibaba.testable.agent.handler;
|
package com.alibaba.testable.agent.handler;
|
||||||
|
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
import com.alibaba.testable.core.util.MockContextUtil;
|
||||||
import com.alibaba.testable.core.util.InvokeRecordUtil;
|
|
||||||
import com.alibaba.testable.core.util.TestableUtil;
|
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,14 +11,11 @@ abstract public class BaseClassWithContextHandler extends BaseClassHandler {
|
|||||||
private static final String CLASS_TESTABLE_TOOL = "com/alibaba/testable/core/tool/TestableTool";
|
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 CLASS_TESTABLE_UTIL = "com/alibaba/testable/core/util/TestableUtil";
|
||||||
private static final String CLASS_MOCK_CONTEXT_HOLDER = "com/alibaba/testable/agent/model/MockContextHolder";
|
private static final String CLASS_MOCK_CONTEXT_HOLDER = "com/alibaba/testable/agent/model/MockContextHolder";
|
||||||
private static final String FIELD_TEST_CASE = "TEST_CASE";
|
|
||||||
private static final String FIELD_SOURCE_METHOD = "SOURCE_METHOD";
|
private static final String FIELD_SOURCE_METHOD = "SOURCE_METHOD";
|
||||||
private static final String FIELD_MOCK_CONTEXT = "MOCK_CONTEXT";
|
private static final String FIELD_MOCK_CONTEXT = "MOCK_CONTEXT";
|
||||||
private static final String FIELD_PARAMETERS = "parameters";
|
private static final String FIELD_PARAMETERS = "parameters";
|
||||||
private static final String METHOD_CURRENT_TEST_CASE_NAME = "currentTestCaseName";
|
|
||||||
private static final String METHOD_CURRENT_SOURCE_METHOD_NAME = "currentSourceMethodName";
|
private static final String METHOD_CURRENT_SOURCE_METHOD_NAME = "currentSourceMethodName";
|
||||||
private static final String METHOD_GET_TEST_CASE_MARK = "getTestCaseMark";
|
private static final String METHOD_GET_TEST_CASE_MARK = "getTestCaseMark";
|
||||||
private static final String SIGNATURE_CURRENT_TEST_CASE_NAME = "(Ljava/lang/String;)Ljava/lang/String;";
|
|
||||||
private static final String SIGNATURE_CURRENT_SOURCE_METHOD_NAME = "()Ljava/lang/String;";
|
private static final String SIGNATURE_CURRENT_SOURCE_METHOD_NAME = "()Ljava/lang/String;";
|
||||||
private static final String SIGNATURE_GET_TEST_CASE_MARK = "()Ljava/lang/String;";
|
private static final String SIGNATURE_GET_TEST_CASE_MARK = "()Ljava/lang/String;";
|
||||||
private static final String SIGNATURE_PARAMETERS = "Ljava/util/Map;";
|
private static final String SIGNATURE_PARAMETERS = "Ljava/util/Map;";
|
||||||
@ -42,18 +37,13 @@ abstract public class BaseClassWithContextHandler extends BaseClassHandler {
|
|||||||
|
|
||||||
private boolean isTestableUtilField(FieldInsnNode fieldInsnNode) {
|
private boolean isTestableUtilField(FieldInsnNode fieldInsnNode) {
|
||||||
return fieldInsnNode.owner.equals(CLASS_TESTABLE_TOOL) &&
|
return fieldInsnNode.owner.equals(CLASS_TESTABLE_TOOL) &&
|
||||||
(fieldInsnNode.name.equals(FIELD_TEST_CASE) || fieldInsnNode.name.equals(FIELD_SOURCE_METHOD) ||
|
(fieldInsnNode.name.equals(FIELD_SOURCE_METHOD) || fieldInsnNode.name.equals(FIELD_MOCK_CONTEXT));
|
||||||
fieldInsnNode.name.equals(FIELD_MOCK_CONTEXT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractInsnNode[] replaceTestableUtilField(ClassNode cn, MethodNode mn, AbstractInsnNode[] instructions,
|
private AbstractInsnNode[] replaceTestableUtilField(ClassNode cn, MethodNode mn, AbstractInsnNode[] instructions,
|
||||||
String fieldName, int pos) {
|
String fieldName, int pos) {
|
||||||
InsnList il = new InsnList();
|
InsnList il = new InsnList();
|
||||||
if (FIELD_TEST_CASE.equals(fieldName)) {
|
if (FIELD_SOURCE_METHOD.equals(fieldName)) {
|
||||||
il.add(new LdcInsnNode(ClassUtil.toDotSeparatedName(cn.name)));
|
|
||||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, METHOD_CURRENT_TEST_CASE_NAME,
|
|
||||||
SIGNATURE_CURRENT_TEST_CASE_NAME, false));
|
|
||||||
} else if (FIELD_SOURCE_METHOD.equals(fieldName)) {
|
|
||||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, METHOD_CURRENT_SOURCE_METHOD_NAME,
|
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, METHOD_CURRENT_SOURCE_METHOD_NAME,
|
||||||
SIGNATURE_CURRENT_SOURCE_METHOD_NAME, false));
|
SIGNATURE_CURRENT_SOURCE_METHOD_NAME, false));
|
||||||
} else if (FIELD_MOCK_CONTEXT.equals(fieldName)) {
|
} else if (FIELD_MOCK_CONTEXT.equals(fieldName)) {
|
||||||
@ -71,10 +61,8 @@ abstract public class BaseClassWithContextHandler extends BaseClassHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getTestCaseMark() {
|
public static String getTestCaseMark() {
|
||||||
String clazz = Thread.currentThread().getStackTrace()[InvokeRecordUtil.INDEX_OF_TEST_CLASS].getClassName();
|
String testClass = MockContextUtil.context.get().testClassName;
|
||||||
// TODO: temporary used
|
String testCaseName = MockContextUtil.context.get().testCaseName;
|
||||||
String testClass = clazz.endsWith("Mock") ? clazz.substring(0, clazz.length() - 5) : clazz;
|
|
||||||
String testCaseName = TestableUtil.currentTestCaseName(testClass);
|
|
||||||
return testClass + "::" + testCaseName;
|
return testClass + "::" + testCaseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package com.alibaba.testable.core.matcher;
|
|||||||
import com.alibaba.testable.core.error.VerifyFailedError;
|
import com.alibaba.testable.core.error.VerifyFailedError;
|
||||||
import com.alibaba.testable.core.model.Verification;
|
import com.alibaba.testable.core.model.Verification;
|
||||||
import com.alibaba.testable.core.util.InvokeRecordUtil;
|
import com.alibaba.testable.core.util.InvokeRecordUtil;
|
||||||
|
import com.alibaba.testable.core.util.MockContextUtil;
|
||||||
import com.alibaba.testable.core.util.TestableUtil;
|
import com.alibaba.testable.core.util.TestableUtil;
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
import java.security.InvalidParameterException;
|
||||||
@ -26,8 +27,8 @@ public class InvokeVerifier {
|
|||||||
* @return the verifier object
|
* @return the verifier object
|
||||||
*/
|
*/
|
||||||
public static InvokeVerifier verify(String mockMethodName) {
|
public static InvokeVerifier verify(String mockMethodName) {
|
||||||
String testClass = Thread.currentThread().getStackTrace()[InvokeRecordUtil.INDEX_OF_TEST_CLASS].getClassName();
|
String testClass = MockContextUtil.context.get().testClassName;
|
||||||
String testCaseName = TestableUtil.currentTestCaseName(testClass);
|
String testCaseName = MockContextUtil.context.get().testCaseName;
|
||||||
String recordIdentify = InvokeRecordUtil.getInvokeIdentify(mockMethodName, testClass, testCaseName);
|
String recordIdentify = InvokeRecordUtil.getInvokeIdentify(mockMethodName, testClass, testCaseName);
|
||||||
return new InvokeVerifier(InvokeRecordUtil.getInvokeRecord(recordIdentify));
|
return new InvokeVerifier(InvokeRecordUtil.getInvokeRecord(recordIdentify));
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,6 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class TestableTool {
|
public class TestableTool {
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of current test case method
|
|
||||||
* @deprecated prefer using `MOCK_CONTEXT` to distinguish test cases
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static String TEST_CASE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the last visited method in source class
|
* Name of the last visited method in source class
|
||||||
*/
|
*/
|
||||||
|
@ -30,10 +30,8 @@ public class InvokeRecordUtil {
|
|||||||
public static void recordMockInvoke(Object[] args, boolean isConstructor, boolean isTargetClassInParameter) {
|
public static void recordMockInvoke(Object[] args, boolean isConstructor, boolean isTargetClassInParameter) {
|
||||||
StackTraceElement mockMethodTraceElement = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS];
|
StackTraceElement mockMethodTraceElement = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS];
|
||||||
String mockMethodName = mockMethodTraceElement.getMethodName();
|
String mockMethodName = mockMethodTraceElement.getMethodName();
|
||||||
String mockClass = mockMethodTraceElement.getClassName();
|
String testClass = MockContextUtil.context.get().testClassName;
|
||||||
// TODO: temporary used
|
String testCaseName = MockContextUtil.context.get().testCaseName;
|
||||||
String testClass = mockClass.substring(0, mockClass.length() - 5);
|
|
||||||
String testCaseName = TestableUtil.currentTestCaseName(testClass);
|
|
||||||
String identify = getInvokeIdentify(mockMethodName, testClass, testCaseName);
|
String identify = getInvokeIdentify(mockMethodName, testClass, testCaseName);
|
||||||
List<Object[]> records = getInvokeRecord(identify);
|
List<Object[]> records = getInvokeRecord(identify);
|
||||||
if (isConstructor) {
|
if (isConstructor) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.alibaba.testable.core.util;
|
package com.alibaba.testable.core.util;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author flin
|
* @author flin
|
||||||
@ -24,27 +23,6 @@ public class TestableUtil {
|
|||||||
return Thread.currentThread().getStackTrace()[INDEX_OF_SOURCE_METHOD].getMethodName();
|
return Thread.currentThread().getStackTrace()[INDEX_OF_SOURCE_METHOD].getMethodName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current test case method
|
|
||||||
* @param testClassName name of current test class
|
|
||||||
* @return method name
|
|
||||||
*/
|
|
||||||
public static String currentTestCaseName(String testClassName) {
|
|
||||||
// try current thread
|
|
||||||
String testCaseName = findFirstMethodFromTestClass(testClassName, Thread.currentThread().getStackTrace());
|
|
||||||
if (testCaseName.isEmpty()) {
|
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
|
||||||
// travel all possible threads
|
|
||||||
for (Thread t : threads) {
|
|
||||||
testCaseName = findFirstMethodFromTestClass(testClassName, t.getStackTrace());
|
|
||||||
if (!testCaseName.isEmpty()) {
|
|
||||||
return testCaseName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return testCaseName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get file name and line number of where current method was called
|
* Get file name and line number of where current method was called
|
||||||
* @return in "filename:linenumber" format
|
* @return in "filename:linenumber" format
|
||||||
@ -54,31 +32,4 @@ public class TestableUtil {
|
|||||||
return stack.getFileName() + ":" + stack.getLineNumber();
|
return stack.getFileName() + ":" + stack.getLineNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String findFirstMethodFromTestClass(String testClassName, StackTraceElement[] stack) {
|
|
||||||
for (int i = stack.length - 1; i >= 0; i--) {
|
|
||||||
if (getOuterClassName(stack[i].getClassName()).equals(testClassName)) {
|
|
||||||
return stack[i].getClassName().indexOf('$') > 0 ?
|
|
||||||
// test case using async call
|
|
||||||
getMethodNameFromLambda(stack[i].getClassName()) :
|
|
||||||
// in case of lambda method
|
|
||||||
getMethodNameFromLambda(stack[i].getMethodName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getMethodNameFromLambda(String originName) {
|
|
||||||
int beginOfMethodName = originName.indexOf('$');
|
|
||||||
if (beginOfMethodName < 0) {
|
|
||||||
return originName;
|
|
||||||
}
|
|
||||||
int endOfMethodName = originName.indexOf('$', beginOfMethodName + 1);
|
|
||||||
return originName.substring(beginOfMethodName + 1, endOfMethodName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getOuterClassName(String className) {
|
|
||||||
int posOfInnerClass = className.indexOf('$');
|
|
||||||
return posOfInnerClass > 0 ? className.substring(0, posOfInnerClass) : className;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
package com.alibaba.testable.core.util;
|
|
||||||
|
|
||||||
import com.alibaba.testable.core.accessor.PrivateAccessor;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class TestableUtilTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_get_outer_class_name() {
|
|
||||||
assertEquals("com.alibaba.testable.demo.DemoMockTest",
|
|
||||||
PrivateAccessor.<String>invokeStatic(TestableUtil.class, "getOuterClassName", "com.alibaba.testable.demo.DemoMockTest$should_able_to_get_source_method_name$1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void should_get_method_name_from_lambda_class_or_method() {
|
|
||||||
assertEquals("should_able_to_get_source_method_name",
|
|
||||||
PrivateAccessor.<String>invokeStatic(TestableUtil.class, "getMethodNameFromLambda", "com.alibaba.testable.demo.DemoMockTest$should_able_to_get_source_method_name$1"));
|
|
||||||
assertEquals("should_able_to_get_source_method_name",
|
|
||||||
PrivateAccessor.<String>invokeStatic(TestableUtil.class, "getMethodNameFromLambda", "lambda$should_able_to_get_source_method_name$0"));
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user