mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-10 20:30:11 +08:00
add association check method
This commit is contained in:
parent
dab1d36a81
commit
c364e96f77
@ -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";
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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";
|
||||
|
||||
}
|
@ -9,6 +9,9 @@ import java.util.Map;
|
||||
|
||||
public class MockContext {
|
||||
|
||||
/**
|
||||
* dot separated class name
|
||||
*/
|
||||
public final String testClassName;
|
||||
|
||||
public final String testCaseName;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user