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 45f3d6b..91d0001 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,11 +7,8 @@ import com.alibaba.testable.core.constant.ConstPool;
*/
public class TestableUtil {
- public static String sourceMemberMethodName(Object testClassRef) {
- return sourceMemberMethodName(testClassRef.getClass());
- }
-
- public static String sourceMemberMethodName(Class> testClass) {
+ 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());
@@ -24,10 +21,7 @@ public class TestableUtil {
}
public static String currentTestCaseName(Object testClassRef) {
- return currentTestCaseName(testClassRef.getClass());
- }
-
- public static String currentTestCaseName(Class> testClass) {
+ Class> testClass = testClassRef.getClass();
StackTraceElement[] stack = getMainThread().getStackTrace();
String testClassName = getRealClassName(testClass);
for (int i = stack.length - 1; i >= 0; i--) {
diff --git a/demo/java-demo/pom.xml b/demo/java-demo/pom.xml
new file mode 100644
index 0000000..9583949
--- /dev/null
+++ b/demo/java-demo/pom.xml
@@ -0,0 +1,93 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.4.RELEASE
+
+
+ com.alibaba.testable
+ java-demo
+ 1.0.0-SNAPSHOT
+ java-demo
+ Demo project for Spring Boot
+
+
+ 1.8
+ 0.1.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ com.alibaba.testable
+ core
+ ${testable.version}
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+ org.ow2.asm
+ asm
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ @{argLine} -javaagent:${settings.localRepository}/com/alibaba/testable/agent/${testable.version}/agent-${testable.version}.jar
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.6
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+ target/jacoco.exec
+ target/jacoco-ut
+
+
+
+
+
+
+
+
diff --git a/demo/java-demo/src/main/java/com/alibaba/testable/demo/BlackBox.java b/demo/java-demo/src/main/java/com/alibaba/testable/demo/BlackBox.java
new file mode 100644
index 0000000..9fa953c
--- /dev/null
+++ b/demo/java-demo/src/main/java/com/alibaba/testable/demo/BlackBox.java
@@ -0,0 +1,15 @@
+package com.alibaba.testable.demo;
+
+public class BlackBox {
+
+ private String data;
+
+ public BlackBox(String data) {
+ this.data = data;
+ }
+
+ public String callMe() {
+ return data;
+ }
+
+}
diff --git a/demo/java-demo/src/main/java/com/alibaba/testable/demo/DemoApplication.java b/demo/java-demo/src/main/java/com/alibaba/testable/demo/DemoApplication.java
new file mode 100644
index 0000000..993f22d
--- /dev/null
+++ b/demo/java-demo/src/main/java/com/alibaba/testable/demo/DemoApplication.java
@@ -0,0 +1,12 @@
+package com.alibaba.testable.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(DemoApplication.class, args);
+ }
+}
diff --git a/demo/java-demo/src/main/java/com/alibaba/testable/demo/DemoService.java b/demo/java-demo/src/main/java/com/alibaba/testable/demo/DemoService.java
new file mode 100644
index 0000000..467be9c
--- /dev/null
+++ b/demo/java-demo/src/main/java/com/alibaba/testable/demo/DemoService.java
@@ -0,0 +1,66 @@
+package com.alibaba.testable.demo;
+
+import org.springframework.stereotype.Service;
+import sun.net.www.http.HttpClient;
+
+import java.net.URL;
+
+@Service
+public class DemoService {
+
+ private int count;
+
+ /**
+ * Target 1 - private method
+ */
+ private String privateFunc(String s, int i) {
+ return s + " - " + i;
+ }
+
+ /**
+ * Target 2 - method with private field access
+ */
+ public String privateFieldAccessFunc() {
+ count += 2;
+ return String.valueOf(count);
+ }
+
+ /**
+ * Target 3 - method with new operation
+ */
+ public String newFunc() {
+ BlackBox component = new BlackBox("something");
+ return component.callMe();
+ }
+
+ /**
+ * Target 4 - method with member method invoke
+ */
+ public String outerFunc(String s) throws Exception {
+ return "{ \"res\": \"" + innerFunc(s) + "\"}";
+ }
+
+ /**
+ * Target 5 - method with common method invoke
+ */
+ public String commonFunc() {
+ return "anything".trim() + "__" + "anything".substring(1, 2) + "__" + "abc".startsWith("ab");
+ }
+
+ public String callerOne() {
+ return callFromDifferentMethod();
+ }
+
+ public String callerTwo() {
+ return callFromDifferentMethod();
+ }
+
+ private String innerFunc(String s) throws Exception {
+ return HttpClient.New(new URL("http:/xxx/" + s)).getURLFile();
+ }
+
+ private String callFromDifferentMethod() {
+ return "realOne";
+ }
+
+}
diff --git a/demo/java-demo/src/main/resources/application.properties b/demo/java-demo/src/main/resources/application.properties
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/demo/java-demo/src/main/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoApplicationTests.java b/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoApplicationTests.java
new file mode 100644
index 0000000..763c918
--- /dev/null
+++ b/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoApplicationTests.java
@@ -0,0 +1,13 @@
+package com.alibaba.testable.demo;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class DemoApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
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
new file mode 100644
index 0000000..204fb6f
--- /dev/null
+++ b/demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoServiceTest.java
@@ -0,0 +1,104 @@
+package com.alibaba.testable.demo;
+
+import com.alibaba.testable.core.accessor.PrivateAccessor;
+import com.alibaba.testable.core.annotation.EnableTestable;
+import com.alibaba.testable.core.annotation.TestableInject;
+import com.alibaba.testable.core.util.TestableUtil;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.Callable;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@EnableTestable
+class DemoServiceTest {
+
+ @TestableInject
+ private BlackBox createBlackBox(String text) {
+ return new BlackBox("mock_" + text);
+ }
+
+ @TestableInject
+ private String innerFunc(String text) {
+ return "mock_" + text;
+ }
+
+ @TestableInject(targetClass="java.lang.String")
+ private String trim(String self) {
+ return "trim_string";
+ }
+
+ @TestableInject(targetClass="java.lang.String", targetMethod = "substring")
+ private String sub(String self, int i, int j) {
+ return "sub_string";
+ }
+
+ @TestableInject(targetClass="java.lang.String")
+ private boolean startsWith(String self, String s) {
+ return false;
+ }
+
+ @TestableInject
+ private String callFromDifferentMethod() {
+ switch (TestableUtil.currentSourceMethodName(this)) {
+ case "callerOne": return "mock_one";
+ default: return "mock_others";
+ }
+ }
+
+ private DemoService demoService = new DemoService();
+
+ @Test
+ void should_able_to_test_private_method() throws Exception {
+ assertEquals("hello - 1", demoService.privateFunc("hello", 1));
+ assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1));
+ }
+
+ @Test
+ void should_able_to_test_private_field() throws Exception {
+ demoService.count = 2;
+ assertEquals("4", demoService.privateFieldAccessFunc());
+ PrivateAccessor.set(demoService, "count", 3);
+ assertEquals("5", demoService.privateFieldAccessFunc());
+ assertEquals(new Integer(5), PrivateAccessor.get(demoService, "count"));
+ }
+
+ @Test
+ void should_able_to_test_new_object() throws Exception {
+ assertEquals("mock_something", demoService.newFunc());
+ }
+
+ @Test
+ void should_able_to_test_member_method() throws Exception {
+ assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"));
+ }
+
+ @Test
+ void should_able_to_test_common_method() throws Exception {
+ assertEquals("trim_string__sub_string__false", demoService.commonFunc());
+ }
+
+ @Test
+ void should_able_to_get_source_method_name() throws Exception {
+ assertEquals("mock_one", demoService.callerOne());
+ assertEquals("mock_others", demoService.callerTwo());
+ assertEquals("mock_one_mock_others", new Callable() {
+ @Override
+ public String call() {
+ return demoService.callerOne() + "_" + demoService.callerTwo();
+ }
+ }.call());
+ }
+
+ @Test
+ void should_able_to_get_test_case_name() throws Exception {
+ assertEquals("should_able_to_get_test_case_name", TestableUtil.currentTestCaseName(this));
+ assertEquals("should_able_to_get_test_case_name", new Callable() {
+ @Override
+ public String call() {
+ return TestableUtil.currentTestCaseName(this);
+ }
+ }.call());
+ }
+
+}
diff --git a/demo/kotlin-demo/pom.xml b/demo/kotlin-demo/pom.xml
new file mode 100644
index 0000000..0a5116c
--- /dev/null
+++ b/demo/kotlin-demo/pom.xml
@@ -0,0 +1,122 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.4.RELEASE
+
+
+ com.alibaba.testable
+ kotlin-demo
+ 0.0.1-SNAPSHOT
+ kotlin-demo
+ Demo project for Spring Boot
+
+
+ 1.8
+ 1.3.72
+ 0.1.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ com.alibaba.testable
+ core
+ ${testable.version}
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/test/kotlin
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ @{argLine} -javaagent:${settings.localRepository}/com/alibaba/testable/agent/${testable.version}/agent-${testable.version}.jar
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+
+
+ -Xjsr305=strict
+
+
+ spring
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-allopen
+ ${kotlin.version}
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.6
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+ target/jacoco.exec
+ target/jacoco-ut
+
+
+
+
+
+
+
+
diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/BlackBox.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/BlackBox.kt
new file mode 100644
index 0000000..f7b7295
--- /dev/null
+++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/BlackBox.kt
@@ -0,0 +1,22 @@
+package com.alibaba.testable.demo
+
+
+class BlackBox(private val data: String) {
+
+ fun callMe(): String {
+ return data
+ }
+
+ fun trim(): String {
+ return data.trim()
+ }
+
+ fun substring(from: Int, to: Int): String {
+ return data.substring(from, to)
+ }
+
+ fun startsWith(prefix: String): Boolean {
+ return data.startsWith(prefix)
+ }
+
+}
diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoApplication.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoApplication.kt
new file mode 100644
index 0000000..e2a3ca6
--- /dev/null
+++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoApplication.kt
@@ -0,0 +1,11 @@
+package com.alibaba.testable.demo
+
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+
+@SpringBootApplication
+class DemoApplication
+
+fun main(args: Array) {
+ runApplication(*args)
+}
diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoService.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoService.kt
new file mode 100644
index 0000000..61125b3
--- /dev/null
+++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoService.kt
@@ -0,0 +1,61 @@
+package com.alibaba.testable.demo
+
+import org.springframework.stereotype.Service
+import sun.net.www.http.HttpClient
+import java.net.URL
+
+
+@Service
+class DemoService {
+
+ private var count = 0
+
+ /**
+ * Target 1 - private method
+ */
+ private fun privateFunc(s: String, i: Int): String {
+ return "$s - $i"
+ }
+
+ /**
+ * Target 2 - method with private field access
+ */
+ fun privateFieldAccessFunc(): String {
+ count += 2
+ return count.toString()
+ }
+
+ /**
+ * Target 3 - method with new operation
+ */
+ fun newFunc(): String {
+ return BlackBox("something").callMe()
+ }
+
+ /**
+ * Target 4 - method with member method invoke
+ */
+ fun outerFunc(s: String): String {
+ return "{ \"res\": \"" + innerFunc(s) + "\"}"
+ }
+
+ /**
+ * Target 5 - method with common method invoke
+ */
+ fun commonFunc(): String {
+ val box = BlackBox("anything")
+ return box.trim() + "__" + box.substring(1, 2) + "__" + box.startsWith("any")
+ }
+
+ fun callerOne(): String {
+ return callFromDifferentMethod()
+ }
+
+ fun callerTwo(): String {
+ return callFromDifferentMethod()
+ }
+
+ private fun innerFunc(s: String) = HttpClient.New(URL("http:/xxx/$s")).urlFile
+
+ private fun callFromDifferentMethod() = "realOne"
+}
diff --git a/demo/kotlin-demo/src/main/resources/application.properties b/demo/kotlin-demo/src/main/resources/application.properties
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/demo/kotlin-demo/src/main/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoApplicationTests.kt b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoApplicationTests.kt
new file mode 100644
index 0000000..b64004c
--- /dev/null
+++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoApplicationTests.kt
@@ -0,0 +1,13 @@
+package com.alibaba.testable.demo
+
+import org.junit.jupiter.api.Test
+import org.springframework.boot.test.context.SpringBootTest
+
+@SpringBootTest
+class DemoApplicationTests {
+
+ @Test
+ fun contextLoads() {
+ }
+
+}
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
new file mode 100644
index 0000000..de9bbe8
--- /dev/null
+++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoServiceTest.kt
@@ -0,0 +1,81 @@
+package com.alibaba.testable.demo
+
+import com.alibaba.testable.core.accessor.PrivateAccessor
+import com.alibaba.testable.core.annotation.EnableTestable
+import com.alibaba.testable.core.annotation.TestableInject
+import com.alibaba.testable.core.util.TestableUtil
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import java.util.concurrent.Callable
+
+
+@EnableTestable
+internal class DemoServiceTest {
+
+ @TestableInject
+ private fun createBlackBox(text: String) = BlackBox("mock_$text")
+
+ @TestableInject
+ private fun innerFunc(text: String) = "mock_$text"
+
+ @TestableInject(targetClass = "com.alibaba.testable.demo.BlackBox")
+ private fun trim(self: BlackBox) = "trim_string"
+
+ @TestableInject(targetClass = "com.alibaba.testable.demo.BlackBox", targetMethod = "substring")
+ private fun sub(self: BlackBox, i: Int, j: Int) = "sub_string"
+
+ @TestableInject(targetClass = "com.alibaba.testable.demo.BlackBox")
+ private fun startsWith(self: BlackBox, s: String) = false
+
+ @TestableInject
+ private fun callFromDifferentMethod() = when (TestableUtil.currentSourceMethodName(this)) {
+ "callerOne" -> "mock_one"
+ else -> "mock_others"
+ }
+
+ private val demoService = DemoService()
+
+ @Test
+ fun should_able_to_test_private_method() {
+ Assertions.assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1))
+ }
+
+ @Test
+ fun should_able_to_test_private_field() {
+ PrivateAccessor.set(demoService, "count", 3)
+ Assertions.assertEquals("5", demoService.privateFieldAccessFunc())
+ Assertions.assertEquals(5, PrivateAccessor.get(demoService, "count"))
+ }
+
+ @Test
+ fun should_able_to_test_new_object() {
+ Assertions.assertEquals("mock_something", demoService.newFunc())
+ }
+
+ @Test
+ fun should_able_to_test_member_method() {
+ Assertions.assertEquals("{ \"res\": \"mock_hello\"}", demoService.outerFunc("hello"))
+ }
+
+ @Test
+ fun should_able_to_test_common_method() {
+ Assertions.assertEquals("trim_string__sub_string__false", demoService.commonFunc())
+ }
+
+ @Test
+ fun should_able_to_get_source_method_name() {
+ Assertions.assertEquals("mock_one", demoService.callerOne())
+ Assertions.assertEquals("mock_others", demoService.callerTwo())
+ Assertions.assertEquals("mock_one_mock_others", Callable {
+ demoService.callerOne() + "_" + demoService.callerTwo()
+ }.call())
+ }
+
+ @Test
+ fun should_able_to_get_test_case_name() {
+ Assertions.assertEquals("should_able_to_get_test_case_name", TestableUtil.currentTestCaseName(this))
+ Assertions.assertEquals("should_able_to_get_test_case_name", Callable {
+ TestableUtil.currentTestCaseName(this)
+ }.call())
+ }
+}
diff --git a/demo/pom.xml b/demo/pom.xml
new file mode 100755
index 0000000..bee3c09
--- /dev/null
+++ b/demo/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ com.alibaba.testable
+ demo
+ 1.0.0-SNAPSHOT
+ pom
+ demos
+
+
+ java-demo
+ kotlin-demo
+
+
+
diff --git a/pom.xml b/pom.xml
index 9323b88..1c30c15 100755
--- a/pom.xml
+++ b/pom.xml
@@ -11,6 +11,7 @@
agent
core
+ demo