diff --git a/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoServiceTest.java b/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoServiceTest.java index b9030b7..ee4c8f2 100644 --- a/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoServiceTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoServiceTest.java @@ -13,33 +13,33 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @EnablePrivateAccess class DemoServiceTest { - @TestableMock + @TestableMock(targetMethod = CONSTRUCTOR) private BlackBox createBlackBox(String text) { return new BlackBox("mock_" + text); } @TestableMock - private String innerFunc(String text) { + private String innerFunc(DemoService self, String text) { return "mock_" + text; } - @TestableMock(targetClass = String.class) + @TestableMock private String trim(String self) { return "trim_string"; } - @TestableMock(targetClass = String.class, targetMethod = "substring") + @TestableMock(targetMethod = "substring") private String sub(String self, int i, int j) { return "sub_string"; } - @TestableMock(targetClass = String.class) + @TestableMock private boolean startsWith(String self, String s) { return false; } @TestableMock - private String callFromDifferentMethod() { + private String callFromDifferentMethod(DemoService self) { if (TEST_CASE.equals("should_able_to_get_test_case_name")) { return "mock_special"; } diff --git a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoServiceTest.kt b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoServiceTest.kt index 4dc4fc3..8924af8 100644 --- a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoServiceTest.kt +++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoServiceTest.kt @@ -12,23 +12,23 @@ import java.util.concurrent.Executors @EnablePrivateAccess internal class DemoServiceTest { - @TestableMock + @TestableMock(targetMethod = CONSTRUCTOR) private fun createBlackBox(text: String) = BlackBox("mock_$text") @TestableMock - private fun innerFunc(text: String) = "mock_$text" + private fun innerFunc(self: DemoService, text: String) = "mock_$text" - @TestableMock(targetClass = BlackBox::class) + @TestableMock private fun trim(self: BlackBox) = "trim_string" - @TestableMock(targetClass = BlackBox::class, targetMethod = "substring") + @TestableMock(targetMethod = "substring") private fun sub(self: BlackBox, i: Int, j: Int) = "sub_string" - @TestableMock(targetClass = BlackBox::class) + @TestableMock private fun startsWith(self: BlackBox, s: String) = false @TestableMock - private fun callFromDifferentMethod(): String { + private fun callFromDifferentMethod(self: DemoService): String { return if (TEST_CASE == "should_able_to_get_test_case_name") { "mock_special" } else { diff --git a/docs/ReleaseNote.md b/docs/ReleaseNote.md index a905c6f..9d15d48 100644 --- a/docs/ReleaseNote.md +++ b/docs/ReleaseNote.md @@ -6,7 +6,7 @@ - remove dependence on EnableTestable annotation in `testable-agent` - rename annotations to reflect the actual use -- ## v0.1.0 +## v0.1.0 - move generated agent jar to class folder - support mock method of any object diff --git a/docs/Usage.md b/docs/Usage.md index 800e13a..d29d84e 100644 --- a/docs/Usage.md +++ b/docs/Usage.md @@ -57,30 +57,30 @@ **【1】覆写任意类的方法调用** -定义一个普通方法,使它与需覆写的方法名称和返回值类型完全一致,且比原方法的参数列表在首位多一个与该方法所属对象类型一致的参数。然后为这个方法加上`@TestableMock`注解,并设置`targetClass`属性值也为该方法所属对象的类型。 +在测试类里定义一个有`@TestableMock`注解的普通方法,使它与需覆写的方法名称、参数、返回值类型完全一致,然后在其参数列表首位再增加一个类型为该方法原本所属对象类型的参数。 -此时被测类中所有对该类指定方法的调用,将在单元测试运行时,自动被替换为对上述自定义Mock方法的调用。 +此时被测类中所有对该需覆写方法的调用,将在单元测试运行时,将自动被替换为对上述自定义Mock方法的调用。 -`@TestableMock`注解还有一个很少需要用到的`targetMethod`属性,用于指定Mock的目标方法名称。使用此参数后被注释修饰的方法名称就可以随意命名了,通常仅在遇到极其罕见的Mock方法签名重名情况时才需要使用。 +**注意**:当遇到有两个需覆写的方法重名时,可将需覆写的方法名写到`@TestableMock`注解的`targetMethod`参数里,此时Mock方法自身就可以随意命名了。 -示例项目文件`DemoServiceTest.java`中的`should_able_to_test_common_method()`用例详细展示了这几种用法。 +示例项目文件`DemoServiceTest.java`中的`should_able_to_test_common_method()`用例详细展示了这种用法。 -**【2】覆写任意类的new操作** +**【2】覆写被测类自身的成员方法** -同样还是定义一个普通方法,然后加上`@TestableMock`注解。方法名称随意,只需让方法的返回值为要覆写new操作的目标类型,且参数与指定类构造方法完全一致。 +有时候,在对某些方法进行测试时,希望将被测类自身的另外一些成员方法Mock掉。 -此时被测类中所有用`new`创建指定类的操作将被替换为对该自定义方法的调用。 - -详见示例项目文件`DemoServiceTest.java`中的`should_able_to_test_new_object()`用例。 - -**【3】覆写被测类自身的私有成员方法** - -有时候,被测类自身的某个成员方法访问了外部系统,在进行单元测试的时候就需要将这个备查样自己的成员方法Mock掉。 - -在测试类中声明一个名称、参数和返回值类型都与要覆写的目标方法完全一致的普通方法,同样加上`@TestableMock`注解,不配置`targetClass`属性,即可实现对被测类私有成员方法的覆写。 +操作方法与前一种情况相同,Mock方法的第一个参数类型需与被测类相同,即可实现对被测类自身(不论是公有或私有)成员方法的覆写。 详见示例项目文件`DemoServiceTest.java`中的`should_able_to_test_member_method()`用例。 +**【3】覆写任意类的new操作** + +在测试类里定义一个有`@TestableMock`注解的普通方法,将注解的`targetMethod`参数写为"<init>",然后使该方法与要被创建类型的构造函数参数、返回值类型完全一致,方法名称随意。 + +此时被测类中所有用`new`创建指定类的操作(并使用了与Mock方法参数一致的构造函数)将被替换为对该自定义方法的调用。 + +详见示例项目文件`DemoServiceTest.java`中的`should_able_to_test_new_object()`用例。 + **【4】识别当前测试用例和调用来源** 在Mock方法中可以通过`TestableTool.TEST_CASE`和`TestableTool.SOURCE_METHOD`来识别**当前运行的测试用例名称**和**进入该Mock方法前的被测类方法名称**,从而区分处理不同的调用场景。 diff --git a/testable-agent/pom.xml b/testable-agent/pom.xml index 5b5ffdd..8b4cdf9 100755 --- a/testable-agent/pom.xml +++ b/testable-agent/pom.xml @@ -14,6 +14,7 @@ <project.compiler.level>1.6</project.compiler.level> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <asm.lib.version>8.0.1</asm.lib.version> + <testable.version>0.2.0-SNAPSHOT</testable.version> <plugin.compiler.version>3.8.1</plugin.compiler.version> <plugin.jar.version>3.2.0</plugin.jar.version> <plugin.shade.version>3.2.4</plugin.shade.version> @@ -31,6 +32,12 @@ <version>5.6.2</version> <scope>test</scope> </dependency> + <dependency> + <groupId>com.alibaba.testable</groupId> + <artifactId>testable-core</artifactId> + <version>${testable.version}</version> + <scope>provided</scope> + </dependency> </dependencies> <build> diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index 6a7629e..933ebbb 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -4,8 +4,6 @@ import com.alibaba.testable.agent.constant.ConstPool; import com.alibaba.testable.agent.model.MethodInfo; import com.alibaba.testable.agent.util.BytecodeUtil; import com.alibaba.testable.agent.util.ClassUtil; -import com.alibaba.testable.agent.util.CollectionUtil; -import com.alibaba.testable.agent.util.StringUtil; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; @@ -35,22 +33,15 @@ public class SourceClassHandler extends BaseClassHandler { */ @Override protected void transform(ClassNode cn) { - List<MethodInfo> methods = new ArrayList<MethodInfo>(); - for (MethodNode m : cn.methods) { - // record all member method names - if (!ConstPool.CONSTRUCTOR.equals(m.name)) { - methods.add(new MethodInfo(cn.name, m.name, null, m.desc)); - } - } - // member methods which has injection stub - Set<MethodInfo> memberInjectMethods = CollectionUtil.getCrossSet(methods, injectMethods); + Set<MethodInfo> memberInjectMethods = new HashSet<MethodInfo>(); + Set<MethodInfo> newOperatorInjectMethods = new HashSet<MethodInfo>(); for (MethodInfo mi : injectMethods) { - if (!mi.getClazz().equals(cn.name)) { + if (mi.getName().equals(ConstPool.CONSTRUCTOR)) { + newOperatorInjectMethods.add(mi); + } else { memberInjectMethods.add(mi); } } - // new operations which has injection stub - Set<MethodInfo> newOperatorInjectMethods = CollectionUtil.getMinusSet(injectMethods, memberInjectMethods); for (MethodNode m : cn.methods) { transformMethod(cn, m, memberInjectMethods, newOperatorInjectMethods); } @@ -64,25 +55,19 @@ public class SourceClassHandler extends BaseClassHandler { do { if (invokeOps.contains(instructions[i].getOpcode())) { MethodInsnNode node = (MethodInsnNode)instructions[i]; - int index = memberInjectMethodList.indexOf(new MethodInfo(node.owner, node.name, null, node.desc)); - if (index >= 0) { + String memberInjectMethodName = getMemberInjectMethodName(memberInjectMethodList, node); + if (memberInjectMethodName != null) { // it's a member method and an inject method for it exist int rangeStart = getMemberMethodStart(instructions, i); if (rangeStart >= 0) { - if (cn.name.equals(node.owner)) { - // member method of current class - instructions = replaceMemberCallOps(cn, mn, instructions, rangeStart, i); - } else { - // member method of other class - String method = memberInjectMethodList.get(index).getSubstitutionMethod(); - instructions = replaceCommonCallOps(cn, mn, instructions, node.owner, method, rangeStart, i); - } + instructions = replaceMemberCallOps(cn, mn, instructions, node.owner, memberInjectMethodName, rangeStart, i); i = rangeStart; } } else if (ConstPool.CONSTRUCTOR.equals(node.name)) { // it's a new operation String newOperatorInjectMethodName = getNewOperatorInjectMethodName(newOperatorInjectMethods, node); - if (newOperatorInjectMethodName.length() > 0) { + if (newOperatorInjectMethodName != null) { + // and an inject method for it exist int rangeStart = getConstructorStart(instructions, node.owner, i); if (rangeStart >= 0) { instructions = replaceNewOps(cn, mn, newOperatorInjectMethodName, instructions, rangeStart, i); @@ -95,13 +80,22 @@ public class SourceClassHandler extends BaseClassHandler { } while (i < instructions.length); } + private String getMemberInjectMethodName(List<MethodInfo> memberInjectMethodList, MethodInsnNode node) { + for (MethodInfo m : memberInjectMethodList) { + if (m.getClazz().equals(node.owner) && m.getName().equals(node.name) && m.getDesc().equals(node.desc)) { + return m.getMockName(); + } + } + return null; + } + private String getNewOperatorInjectMethodName(Set<MethodInfo> newOperatorInjectMethods, MethodInsnNode node) { for (MethodInfo m : newOperatorInjectMethods) { if (m.getDesc().equals(getConstructorInjectDesc(node))) { - return m.getName(); + return m.getMockName(); } } - return ""; + return null; } private String getConstructorInjectDesc(MethodInsnNode constructorNode) { @@ -160,19 +154,6 @@ public class SourceClassHandler extends BaseClassHandler { } private AbstractInsnNode[] replaceMemberCallOps(ClassNode cn, MethodNode mn, AbstractInsnNode[] instructions, - int start, int end) { - MethodInsnNode method = (MethodInsnNode)instructions[end]; - String testClassName = ClassUtil.getTestClassName(cn.name); - mn.instructions.insertBefore(instructions[start], new FieldInsnNode(GETSTATIC, testClassName, - ConstPool.TESTABLE_INJECT_REF, ClassUtil.toByteCodeClassName(testClassName))); - mn.instructions.insertBefore(instructions[end], new MethodInsnNode(INVOKEVIRTUAL, testClassName, - method.name, method.desc, false)); - mn.instructions.remove(instructions[start]); - mn.instructions.remove(instructions[end]); - return mn.instructions.toArray(); - } - - private AbstractInsnNode[] replaceCommonCallOps(ClassNode cn, MethodNode mn, AbstractInsnNode[] instructions, String ownerClass, String substitutionMethod, int start, int end) { mn.maxStack++; MethodInsnNode method = (MethodInsnNode)instructions[end]; diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/model/MethodInfo.java b/testable-agent/src/main/java/com/alibaba/testable/agent/model/MethodInfo.java index 68acfe1..7ef5ed6 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/model/MethodInfo.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/model/MethodInfo.java @@ -6,27 +6,26 @@ package com.alibaba.testable.agent.model; public class MethodInfo { /** - * name of the class this method belongs to + * name of the class this method belongs to (in slash-separate format) */ private final String clazz; /** - * name of the method + * name of the source method */ private final String name; /** - * name of the substitution method - * Note: this field do NOT join the `equals()` or `hashCode()` calculation + * name of the mock method */ - private final String substitutionMethod; + private final String mockName; /** - * parameter and return value of the method + * parameter and return value of the source method */ private final String desc; - public MethodInfo(String clazz, String name, String substitutionMethod, String desc) { + public MethodInfo(String clazz, String name, String mockName, String desc) { this.clazz = clazz; this.name = name; - this.substitutionMethod = substitutionMethod; + this.mockName = mockName; this.desc = desc; } @@ -38,8 +37,8 @@ public class MethodInfo { return name; } - public String getSubstitutionMethod() { - return substitutionMethod; + public String getMockName() { + return mockName; } public String getDesc() { @@ -55,6 +54,7 @@ public class MethodInfo { if (!clazz.equals(that.clazz)) { return false; } if (!name.equals(that.name)) { return false; } + if (!mockName.equals(that.mockName)) { return false; } return desc.equals(that.desc); } @@ -62,6 +62,7 @@ public class MethodInfo { public int hashCode() { int result = clazz.hashCode(); result = 31 * result + name.hashCode(); + result = 31 * result + mockName.hashCode(); result = 31 * result + desc.hashCode(); return result; } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java index f5c807b..c25cabc 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java @@ -8,7 +8,6 @@ import com.alibaba.testable.agent.model.MethodInfo; import com.alibaba.testable.agent.tool.ComparableWeakRef; import com.alibaba.testable.agent.util.ClassUtil; import org.objectweb.asm.ClassReader; -import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; @@ -22,7 +21,6 @@ import java.util.List; import java.util.Set; import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassName; -import static com.alibaba.testable.agent.util.ClassUtil.toSlashSeparatedName; /** * @author flin @@ -30,7 +28,6 @@ import static com.alibaba.testable.agent.util.ClassUtil.toSlashSeparatedName; public class TestableClassTransformer implements ClassFileTransformer { private final Set<ComparableWeakRef<String>> loadedClassNames = ComparableWeakRef.getWeekHashSet(); - private static final String TARGET_CLASS = "targetClass"; private static final String TARGET_METHOD = "targetMethod"; @Override @@ -85,47 +82,44 @@ public class TestableClassTransformer implements ClassFileTransformer { } private void checkMethodAnnotation(ClassNode cn, List<MethodInfo> methodInfos, MethodNode mn) { - if (mn.visibleAnnotations == null) { + ImmutablePair<String, String> methodDescPair = extractFirstParameter(mn.desc); + if (methodDescPair == null || mn.visibleAnnotations == null) { return; } for (AnnotationNode an : mn.visibleAnnotations) { if (toDotSeparateFullClassName(an.desc).equals(ConstPool.TESTABLE_MOCK)) { - String sourceClassName = ClassUtil.getSourceClassName(cn.name); - String targetClass = getAnnotationParameter(an, TARGET_CLASS, sourceClassName); + String targetClass = ClassUtil.toSlashSeparateFullClassName(methodDescPair.left); String targetMethod = getAnnotationParameter(an, TARGET_METHOD, mn.name); - if (sourceClassName.equals(targetClass)) { - // member method of the source class - methodInfos.add(new MethodInfo( - toSlashSeparatedName(targetClass), targetMethod, null, mn.desc)); + if (targetMethod.equals(ConstPool.CONSTRUCTOR)) { + String sourceClassName = ClassUtil.getSourceClassName(cn.name); + methodInfos.add(new MethodInfo(sourceClassName, targetMethod, mn.name, mn.desc)); } else { - // member method of a common class - ImmutablePair<String, String> methodDescPair = extractFirstParameter(mn.desc); - if (methodDescPair != null && methodDescPair.left.equals(ClassUtil.toByteCodeClassName(targetClass))) { - methodInfos.add(new MethodInfo( - toSlashSeparatedName(targetClass), targetMethod, mn.name, methodDescPair.right)); - } + methodInfos.add(new MethodInfo(targetClass, targetMethod, mn.name, methodDescPair.right)); } break; } } } + /** + * Split desc to "first parameter" and "desc of rest parameters" + * @param desc method desc + */ private ImmutablePair<String, String> extractFirstParameter(String desc) { // assume first parameter is a class int pos = desc.indexOf(";"); return pos < 0 ? null : ImmutablePair.of(desc.substring(1, pos + 1), "(" + desc.substring(pos + 1)); } - private String getAnnotationParameter(AnnotationNode an, String key, String defaultValue) { + /** + * Read value of annotation parameter + */ + private <T> T getAnnotationParameter(AnnotationNode an, String key, T defaultValue) { if (an.values != null) { - int i = an.values.indexOf(key); - if (i % 2 == 0) { - Object value = an.values.get(i + 1); - if (value instanceof Type) { - // fit for `targetClass` parameter - return ClassUtil.toSlashSeparateFullClassName(value.toString()); + for (int i = 0; i < an.values.size(); i += 2) { + if (an.values.get(i).equals(key)) { + return (T)(an.values.get(i + 1)); } - return value.toString(); } } return defaultValue; diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/util/CollectionUtil.java b/testable-agent/src/main/java/com/alibaba/testable/agent/util/CollectionUtil.java index 7656e09..b08222b 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/util/CollectionUtil.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/util/CollectionUtil.java @@ -33,34 +33,4 @@ public class CollectionUtil { return list; } - /** - * Get cross set of two collections - * @param collectionLeft the first collection - * @param collectionRight the second collection - */ - public static <T> Set<T> getCrossSet(Collection<T> collectionLeft, Collection<T> collectionRight) { - Set<T> crossSet = new HashSet<T>(); - for (T i : collectionLeft) { - if (collectionRight.contains(i)) { - crossSet.add(i); - } - } - return crossSet; - } - - /** - * Get minus set of two collections - * @param collectionRaw original collection - * @param collectionMinus items to remove - */ - public static <T> Set<T> getMinusSet(Collection<T> collectionRaw, Collection<T> collectionMinus) { - Set<T> crossSet = new HashSet<T>(); - for (T i : collectionRaw) { - if (!collectionMinus.contains(i)) { - crossSet.add(i); - } - } - return crossSet; - } - } diff --git a/testable-agent/src/test/java/com/alibaba/testable/agent/handler/SourceClassHandlerTest.java b/testable-agent/src/test/java/com/alibaba/testable/agent/handler/SourceClassHandlerTest.java new file mode 100644 index 0000000..71944d3 --- /dev/null +++ b/testable-agent/src/test/java/com/alibaba/testable/agent/handler/SourceClassHandlerTest.java @@ -0,0 +1,38 @@ +package com.alibaba.testable.agent.handler; + +import com.alibaba.testable.agent.model.MethodInfo; +import com.alibaba.testable.core.accessor.PrivateAccessor; +import org.junit.jupiter.api.Test; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.*; +import static org.objectweb.asm.Opcodes.*; + +class SourceClassHandlerTest { + + private final SourceClassHandler handler = new SourceClassHandler(new ArrayList<MethodInfo>()); + + @Test + void should_get_member_method_start() { + AbstractInsnNode[] instructions = new AbstractInsnNode[]{ + new VarInsnNode(ALOAD, 2), + new VarInsnNode(ALOAD, 0), + new VarInsnNode(ALOAD, 2), + new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "trim", "()Ljava/lang/String;", false), + new InsnNode(ICONST_1), + new InsnNode(ICONST_2), + new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;", false), + new MethodInsnNode(INVOKESPECIAL, "com/alibaba/testable/demo/DemoServiceTest", "blackBox", "(Ljava/lang/String;)Lcom/alibaba/testable/demo/BlackBox;", false), + new MethodInsnNode(INVOKEVIRTUAL, "com/alibaba/testable/demo/BlackBox", "callMe", "()Ljava/lang/String;", false), + new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "startsWith", "(Ljava/lang/String;)Z", false) + }; + assertEquals(2, PrivateAccessor.invoke(handler, "getMemberMethodStart", instructions, 3)); + assertEquals(2, PrivateAccessor.invoke(handler, "getMemberMethodStart", instructions, 6)); + assertEquals(0, PrivateAccessor.invoke(handler, "getMemberMethodStart", instructions, 9)); + } +} diff --git a/testable-agent/src/test/java/com/alibaba/testable/agent/transformer/TestableClassTransformerTest.java b/testable-agent/src/test/java/com/alibaba/testable/agent/transformer/TestableClassTransformerTest.java new file mode 100644 index 0000000..d1c29e0 --- /dev/null +++ b/testable-agent/src/test/java/com/alibaba/testable/agent/transformer/TestableClassTransformerTest.java @@ -0,0 +1,23 @@ +package com.alibaba.testable.agent.transformer; + +import com.alibaba.testable.core.accessor.PrivateAccessor; +import org.junit.jupiter.api.Test; +import org.objectweb.asm.tree.AnnotationNode; + +import static com.alibaba.testable.agent.util.CollectionUtil.listOf; +import static org.junit.jupiter.api.Assertions.*; + +class TestableClassTransformerTest { + + private TestableClassTransformer transformer = new TestableClassTransformer(); + + @Test + void should_get_annotation_parameter() { + AnnotationNode an = new AnnotationNode(""); + an.values = listOf((Object)"testKey", "testValue", "demoKey", "demoValue"); + assertEquals("testValue", PrivateAccessor.invoke(transformer, "getAnnotationParameter", an, "testKey", "none")); + assertEquals("demoValue", PrivateAccessor.invoke(transformer, "getAnnotationParameter", an, "demoKey", "none")); + assertEquals("none", PrivateAccessor.invoke(transformer, "getAnnotationParameter", an, "testValue", "none")); + } + +} diff --git a/testable-core/src/main/java/com/alibaba/testable/core/annotation/TestableMock.java b/testable-core/src/main/java/com/alibaba/testable/core/annotation/TestableMock.java index afb300c..75ded6a 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/annotation/TestableMock.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/annotation/TestableMock.java @@ -12,11 +12,6 @@ import java.lang.annotation.*; @Documented public @interface TestableMock { - /** - * mock method of specified class instead of the class under test - */ - Class targetClass() default Object.class; - /** * mock specified method instead of method with same name */ diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java index fda20ac..5d8d6b7 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java @@ -7,6 +7,11 @@ import com.alibaba.testable.core.util.TestableUtil; */ public class TestableTool { + /** + * Name of the constructor method + */ + public static final String CONSTRUCTOR = "<init>"; + /** * Name of current test case method */