use mock context to fetch current test case

This commit is contained in:
金戟 2021-02-13 14:29:48 +08:00
parent 94c5aa8621
commit 9a22f361f3
9 changed files with 14 additions and 107 deletions

View File

@ -32,7 +32,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>

View File

@ -108,14 +108,13 @@ class DemoMockTest {
}
@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");
// synchronous
assertEquals("mock_special", demoMock.callerOne());
// asynchronous
assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoMock.callerOne()).get());
verify("callFromDifferentMethod").withTimes(2);
MOCK_CONTEXT.clear();
}
}

View File

@ -19,10 +19,11 @@ public class PreMain {
private static final String EQUAL = "=";
public static void premain(String agentArgs, Instrumentation inst) {
// add transmittable thread local transformer
TtlAgent.premain(agentArgs, inst);
// add testable mock transformer
parseArgs(agentArgs);
inst.addTransformer(new TestableClassTransformer());
// add TTL Transformer
TtlAgent.premain(agentArgs, inst);
}
private static void parseArgs(String args) {

View File

@ -1,8 +1,6 @@
package com.alibaba.testable.agent.handler;
import com.alibaba.testable.agent.util.ClassUtil;
import com.alibaba.testable.core.util.InvokeRecordUtil;
import com.alibaba.testable.core.util.TestableUtil;
import com.alibaba.testable.core.util.MockContextUtil;
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_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 FIELD_TEST_CASE = "TEST_CASE";
private static final String FIELD_SOURCE_METHOD = "SOURCE_METHOD";
private static final String FIELD_MOCK_CONTEXT = "MOCK_CONTEXT";
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_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_GET_TEST_CASE_MARK = "()Ljava/lang/String;";
private static final String SIGNATURE_PARAMETERS = "Ljava/util/Map;";
@ -42,18 +37,13 @@ abstract public class BaseClassWithContextHandler extends BaseClassHandler {
private boolean isTestableUtilField(FieldInsnNode fieldInsnNode) {
return fieldInsnNode.owner.equals(CLASS_TESTABLE_TOOL) &&
(fieldInsnNode.name.equals(FIELD_TEST_CASE) || fieldInsnNode.name.equals(FIELD_SOURCE_METHOD) ||
fieldInsnNode.name.equals(FIELD_MOCK_CONTEXT));
(fieldInsnNode.name.equals(FIELD_SOURCE_METHOD) || fieldInsnNode.name.equals(FIELD_MOCK_CONTEXT));
}
private AbstractInsnNode[] replaceTestableUtilField(ClassNode cn, MethodNode mn, AbstractInsnNode[] instructions,
String fieldName, int pos) {
InsnList il = new InsnList();
if (FIELD_TEST_CASE.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)) {
if (FIELD_SOURCE_METHOD.equals(fieldName)) {
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, METHOD_CURRENT_SOURCE_METHOD_NAME,
SIGNATURE_CURRENT_SOURCE_METHOD_NAME, false));
} else if (FIELD_MOCK_CONTEXT.equals(fieldName)) {
@ -71,10 +61,8 @@ abstract public class BaseClassWithContextHandler extends BaseClassHandler {
}
public static String getTestCaseMark() {
String clazz = Thread.currentThread().getStackTrace()[InvokeRecordUtil.INDEX_OF_TEST_CLASS].getClassName();
// TODO: temporary used
String testClass = clazz.endsWith("Mock") ? clazz.substring(0, clazz.length() - 5) : clazz;
String testCaseName = TestableUtil.currentTestCaseName(testClass);
String testClass = MockContextUtil.context.get().testClassName;
String testCaseName = MockContextUtil.context.get().testCaseName;
return testClass + "::" + testCaseName;
}

View File

@ -3,6 +3,7 @@ package com.alibaba.testable.core.matcher;
import com.alibaba.testable.core.error.VerifyFailedError;
import com.alibaba.testable.core.model.Verification;
import com.alibaba.testable.core.util.InvokeRecordUtil;
import com.alibaba.testable.core.util.MockContextUtil;
import com.alibaba.testable.core.util.TestableUtil;
import java.security.InvalidParameterException;
@ -26,8 +27,8 @@ public class InvokeVerifier {
* @return the verifier object
*/
public static InvokeVerifier verify(String mockMethodName) {
String testClass = Thread.currentThread().getStackTrace()[InvokeRecordUtil.INDEX_OF_TEST_CLASS].getClassName();
String testCaseName = TestableUtil.currentTestCaseName(testClass);
String testClass = MockContextUtil.context.get().testClassName;
String testCaseName = MockContextUtil.context.get().testCaseName;
String recordIdentify = InvokeRecordUtil.getInvokeIdentify(mockMethodName, testClass, testCaseName);
return new InvokeVerifier(InvokeRecordUtil.getInvokeRecord(recordIdentify));
}

View File

@ -7,13 +7,6 @@ import java.util.Map;
*/
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
*/

View File

@ -30,10 +30,8 @@ public class InvokeRecordUtil {
public static void recordMockInvoke(Object[] args, boolean isConstructor, boolean isTargetClassInParameter) {
StackTraceElement mockMethodTraceElement = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS];
String mockMethodName = mockMethodTraceElement.getMethodName();
String mockClass = mockMethodTraceElement.getClassName();
// TODO: temporary used
String testClass = mockClass.substring(0, mockClass.length() - 5);
String testCaseName = TestableUtil.currentTestCaseName(testClass);
String testClass = MockContextUtil.context.get().testClassName;
String testCaseName = MockContextUtil.context.get().testCaseName;
String identify = getInvokeIdentify(mockMethodName, testClass, testCaseName);
List<Object[]> records = getInvokeRecord(identify);
if (isConstructor) {

View File

@ -1,6 +1,5 @@
package com.alibaba.testable.core.util;
import java.util.Set;
/**
* @author flin
@ -24,27 +23,6 @@ public class TestableUtil {
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
* @return in "filename:linenumber" format
@ -54,31 +32,4 @@ public class TestableUtil {
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;
}
}

View File

@ -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"));
}
}