diff --git a/agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java b/agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java index 99d26a4..aead738 100644 --- a/agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java +++ b/agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java @@ -83,10 +83,11 @@ public class TestClassHandler extends BaseClassHandler { private AbstractInsnNode[] replaceTestableUtilField(MethodNode mn, AbstractInsnNode[] instructions, String fieldName, int pos) { InsnList insnNodes = new InsnList(); - insnNodes.insert(new VarInsnNode(ALOAD, 0)); + // NOTE: will insert in reversed order insnNodes.insert(new MethodInsnNode(INVOKESTATIC, CLASS_TESTABLE_UTIL, FIELD_TO_METHOD_MAPPING.get(fieldName), SIGNATURE_TESTABLE_UTIL_METHOD, false)); - mn.instructions.insertBefore(instructions[pos], insnNodes); + insnNodes.insert(new VarInsnNode(ALOAD, 0)); + mn.instructions.insert(instructions[pos], insnNodes); mn.instructions.remove(instructions[pos]); return mn.instructions.toArray(); } diff --git a/core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java b/core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java index de3856b..126bae6 100644 --- a/core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java +++ b/core/src/main/java/com/alibaba/testable/core/tool/TestableTool.java @@ -5,7 +5,14 @@ package com.alibaba.testable.core.tool; */ public class TestableTool { + /** + * Name of current test case method + */ public static String TEST_CASE; + + /** + * Name of the last visited method in source class + */ public static String SOURCE_METHOD; } diff --git a/core/src/main/java/com/alibaba/testable/core/util/ResourceUtil.java b/core/src/main/java/com/alibaba/testable/core/util/ResourceUtil.java index f265d39..9e105de 100644 --- a/core/src/main/java/com/alibaba/testable/core/util/ResourceUtil.java +++ b/core/src/main/java/com/alibaba/testable/core/util/ResourceUtil.java @@ -7,6 +7,10 @@ import java.io.*; */ public class ResourceUtil { + /** + * Read content of a text file from resource folder + * @param filePath file to read + */ public static String fetchText(String filePath) { InputStream in = ResourceUtil.class.getResourceAsStream("/" + filePath); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); @@ -24,6 +28,10 @@ public class ResourceUtil { } } + /** + * Read content of a binary file from resource folder + * @param filePath file to read + */ public static byte[] fetchBinary(String filePath) { InputStream in = ResourceUtil.class.getResourceAsStream("/" + filePath); if (in == null) { diff --git a/core/src/main/java/com/alibaba/testable/core/util/StringUtil.java b/core/src/main/java/com/alibaba/testable/core/util/StringUtil.java index cd4b093..c32ea51 100644 --- a/core/src/main/java/com/alibaba/testable/core/util/StringUtil.java +++ b/core/src/main/java/com/alibaba/testable/core/util/StringUtil.java @@ -7,6 +7,11 @@ import java.util.List; */ public class StringUtil { + /** + * Join strings + * @param list strings to join + * @param conjunction connection character + */ static public String join(List list, String conjunction) { StringBuilder sb = new StringBuilder(); diff --git a/core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java b/core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java index 91d0001..ceb4143 100644 --- a/core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java +++ b/core/src/main/java/com/alibaba/testable/core/util/TestableUtil.java @@ -7,19 +7,27 @@ import com.alibaba.testable.core.constant.ConstPool; */ public class TestableUtil { + /** + * Get the last visit method in source file + * @param testClassRef usually `this` variable of the test class + * @return method name + */ public static String currentSourceMethodName(Object testClassRef) { Class testClass = testClassRef.getClass(); - StackTraceElement[] stack = getMainThread().getStackTrace(); String testClassName = getRealClassName(testClass); String sourceClassName = testClassName.substring(0, testClassName.length() - ConstPool.TEST_POSTFIX.length()); - for (int i = stack.length - 1; i >= 0; i--) { - if (stack[i].getClassName().equals(sourceClassName)) { - return stack[i].getMethodName(); - } + String sourceMethod = findLastMethodFromSourceClass(sourceClassName, getMainThread().getStackTrace()); + if (sourceMethod.isEmpty()) { + return findLastMethodFromSourceClass(sourceClassName, Thread.currentThread().getStackTrace()); } - return ""; + return sourceMethod; } + /** + * Get current test case method + * @param testClassRef usually `this` variable of the test class + * @return method name + */ public static String currentTestCaseName(Object testClassRef) { Class testClass = testClassRef.getClass(); StackTraceElement[] stack = getMainThread().getStackTrace(); @@ -32,6 +40,15 @@ public class TestableUtil { return ""; } + private static String findLastMethodFromSourceClass(String sourceClassName, StackTraceElement[] stack) { + for (StackTraceElement element : stack) { + if (element.getClassName().equals(sourceClassName)) { + return element.getMethodName(); + } + } + return ""; + } + private static String getRealClassName(Class testClass) { String className = testClass.getName(); int posOfInnerClass = className.lastIndexOf('$'); @@ -44,6 +61,7 @@ public class TestableUtil { return t; } } + // usually impossible to go here return Thread.currentThread(); } 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 2879c15..02d755e 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 @@ -6,6 +6,7 @@ import com.alibaba.testable.core.annotation.TestableInject; import org.junit.jupiter.api.Test; import java.util.concurrent.Callable; +import java.util.concurrent.Executors; import static com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD; import static com.alibaba.testable.core.tool.TestableTool.TEST_CASE; @@ -41,6 +42,9 @@ class DemoServiceTest { @TestableInject private String callFromDifferentMethod() { + if (TEST_CASE.equals("should_able_to_get_test_case_name")) { + return "mock_special"; + } switch (SOURCE_METHOD) { case "callerOne": return "mock_one"; default: return "mock_others"; @@ -81,15 +85,19 @@ class DemoServiceTest { @Test void should_able_to_get_source_method_name() throws Exception { - assertEquals("mock_one_mock_others", demoService.callerTwo() + "_" + demoService.callerOne()); - assertEquals("mock_one_mock_others", ((Callable)() -> - demoService.callerOne() + "_" + demoService.callerTwo()).call()); + // synchronous + assertEquals("mock_one_mock_others", demoService.callerOne() + "_" + demoService.callerTwo()); + // asynchronous + assertEquals("mock_one_mock_others", + Executors.newSingleThreadExecutor().submit(() -> demoService.callerOne() + "_" + demoService.callerTwo()).get()); } @Test void should_able_to_get_test_case_name() throws Exception { - assertEquals("should_able_to_get_test_case_name", TEST_CASE); - assertEquals("should_able_to_get_test_case_name", ((Callable)() -> TEST_CASE).call()); + // synchronous + assertEquals("mock_special", demoService.callerOne()); + // asynchronous + assertEquals("mock_special", Executors.newSingleThreadExecutor().submit(() -> demoService.callerOne()).get()); } } 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 7554509..854f2fb 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 @@ -7,7 +7,7 @@ import com.alibaba.testable.core.tool.TestableTool.SOURCE_METHOD import com.alibaba.testable.core.tool.TestableTool.TEST_CASE import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import java.util.concurrent.Callable +import java.util.concurrent.Executors @EnableTestable @@ -29,9 +29,15 @@ internal class DemoServiceTest { private fun startsWith(self: BlackBox, s: String) = false @TestableInject - private fun callFromDifferentMethod() = when (SOURCE_METHOD) { - "callerOne" -> "mock_one" - else -> "mock_others" + private fun callFromDifferentMethod(): String { + return if (TEST_CASE == "should_able_to_get_test_case_name") { + "mock_special" + } else { + when (SOURCE_METHOD) { + "callerOne" -> "mock_one" + else -> "mock_others" + } + } } private val demoService = DemoService() @@ -65,15 +71,21 @@ internal class DemoServiceTest { @Test fun should_able_to_get_source_method_name() { - assertEquals("mock_one_mock_others", demoService.callerTwo() + "_" + demoService.callerOne()) - assertEquals("mock_one_mock_others", Callable { + // synchronous + assertEquals("mock_one_mock_others", demoService.callerOne() + "_" + demoService.callerTwo()) + // asynchronous + assertEquals("mock_one_mock_others", Executors.newSingleThreadExecutor().submit { demoService.callerOne() + "_" + demoService.callerTwo() - }.call()) + }.get()) } @Test fun should_able_to_get_test_case_name() { - assertEquals("should_able_to_get_test_case_name", TEST_CASE) - assertEquals("should_able_to_get_test_case_name", Callable { TEST_CASE }.call()) + // synchronous + assertEquals("mock_special", demoService.callerOne()) + // asynchronous + assertEquals("mock_special", Executors.newSingleThreadExecutor().submit { + demoService.callerOne() + }.get()) } }