add association check method

This commit is contained in:
金戟 2021-02-17 17:38:30 +08:00
parent dab1d36a81
commit c364e96f77
8 changed files with 88 additions and 18 deletions

View File

@ -10,9 +10,6 @@ public class ConstPool {
public static final String DOLLAR = "$";
public static final String UNDERLINE = "_";
public static final String TEST_POSTFIX = "Test";
public static final String MOCK_POSTFIX = "Mock";
public static final String FIELD_TARGET_METHOD = "targetMethod";
public static final String FIELD_TARGET_CLASS = "targetClass";

View File

@ -7,14 +7,12 @@ import com.alibaba.testable.agent.handler.TestClassHandler;
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.model.LogLevel;
import com.alibaba.testable.core.util.LogUtil;
import com.alibaba.testable.core.util.MockContextUtil;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.File;
import java.io.FileOutputStream;
@ -25,6 +23,7 @@ import java.util.List;
import static com.alibaba.testable.agent.constant.ConstPool.*;
import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassName;
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
@ -183,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(className);
MockContextUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className));
return ClassUtil.toSlashSeparatedName(mockClassName);
}
// look for Mock inner class

View File

@ -11,6 +11,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
/**
@ -93,7 +95,7 @@ public class ClassUtil {
* @return mock class name
*/
public static String getMockClassName(String sourceClassName) {
return sourceClassName + ConstPool.MOCK_POSTFIX;
return sourceClassName + MOCK_POSTFIX;
}
/**
@ -102,7 +104,7 @@ public class ClassUtil {
* @return test class name
*/
public static String getTestClassName(String sourceClassName) {
return sourceClassName + ConstPool.TEST_POSTFIX;
return sourceClassName + TEST_POSTFIX;
}
/**
@ -111,7 +113,7 @@ public class ClassUtil {
* @return source class name
*/
public static String getSourceClassName(String testClassName) {
return testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length());
return testClassName.substring(0, testClassName.length() - TEST_POSTFIX.length());
}
/**

View File

@ -0,0 +1,8 @@
package com.alibaba.testable.core.constant;
public class ConstPool {
public static final String TEST_POSTFIX = "Test";
public static final String MOCK_POSTFIX = "Mock";
}

View File

@ -9,6 +9,9 @@ import java.util.Map;
public class MockContext {
/**
* dot separated class name
*/
public final String testClassName;
public final String testCaseName;

View File

@ -5,31 +5,40 @@ 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 {
/**
* Container to store context information of each test case
*/
public static InheritableThreadLocal<MockContext> context = new TransmittableThreadLocal<MockContext>();
/**
* mock class referred by @MockWith annotation to list of its test classes
* 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>());
/**
* [0]Thread [1]MockContextUtil [2]TestClass
* [0]Thread [1]MockContextUtil [2]TestClass/MockClass
*/
public static final int INDEX_OF_TEST_CLASS = 2;
public static final int INDEX_OF_INVOKER_CLASS = 2;
/**
* Should be invoked at the beginning of each test case method
* 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_TEST_CLASS].getClassName();
String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS].getMethodName();
String testClassName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getClassName();
String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getMethodName();
context.set(new MockContext(testClassName, testCaseName));
}
/**
* Should be invoked at the end of each test case execution
* Clean up mock context
* should be invoked at the end of each test case execution
*/
public static void clean() {
context.remove();
@ -40,4 +49,30 @@ 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);
}
}

View File

@ -7,11 +7,11 @@ package com.alibaba.testable.core.util;
public class TestableUtil {
/**
* [0]Thread.getStackTrace() [1]currentSourceMethodName() [2]MockMethod -> [3]SourceMethod
* [0]Thread.getStackTrace() [1]currentSourceMethodName() [2]MockMethod [3]SourceMethod
*/
private static final int INDEX_OF_SOURCE_METHOD = 3;
/**
* [0]Thread.getStackTrace() [1]previousStackLocation() [2]Invoker -> [3]Caller of invoker
* [0]Thread.getStackTrace() [1]previousStackLocation() [2]Invoker [3]Caller of invoker
*/
private static final int INDEX_OF_CALLER_METHOD = 3;

View File

@ -0,0 +1,26 @@
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 MockContextUtilTest {
@Test
void should_able_to_associate_by_inner_mock_class() {
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByInnerMockClass",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTest$Mock"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.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",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoMock"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByOuterMockClass",
"com.alibaba.testable.DemoTester", "com.alibaba.testable.DemoMock"));
}
}