From 121536a337db24119afd538d34ebf8f4bf7926cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Thu, 4 Mar 2021 23:41:22 +0800 Subject: [PATCH] support mocking invocation inside an inner class --- .../alibaba/demo/basic/DemoInnerClass.java | 60 +++++++++++++++++++ .../demo/basic/DemoInnerClassTest.java | 29 +++++++++ .../com/alibaba/demo/basic/DemoInnerClass.kt | 49 +++++++++++++++ .../alibaba/demo/basic/DemoInnerClassTest.kt | 28 +++++++++ .../transformer/TestableClassTransformer.java | 11 +++- .../testable/agent/util/MethodUtilTest.java | 10 ++-- 6 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 demo/java-demo/src/main/java/com/alibaba/demo/basic/DemoInnerClass.java create mode 100644 demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoInnerClassTest.java create mode 100644 demo/kotlin-demo/src/main/kotlin/com/alibaba/demo/basic/DemoInnerClass.kt create mode 100644 demo/kotlin-demo/src/test/kotlin/com/alibaba/demo/basic/DemoInnerClassTest.kt diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/basic/DemoInnerClass.java b/demo/java-demo/src/main/java/com/alibaba/demo/basic/DemoInnerClass.java new file mode 100644 index 0000000..b592fbf --- /dev/null +++ b/demo/java-demo/src/main/java/com/alibaba/demo/basic/DemoInnerClass.java @@ -0,0 +1,60 @@ +package com.alibaba.demo.basic; + +import java.util.concurrent.*; + +/** + * 演示对内部类的Mock支持 + * Demonstrate support for mocking invocation inside a inner class + */ +public class DemoInnerClass { + + public static class StaticInner { + /** + * invocation inside a static inner class + */ + public String demo() { + return methodToBeMock(); + } + } + + public class Inner { + /** + * invocation inside a non-static inner class + */ + public String demo() { + return methodToBeMock(); + } + } + + private ExecutorService executorService = Executors.newSingleThreadExecutor(); + + public String callAnonymousInner() throws ExecutionException, InterruptedException { + /** + * invocation inside a anonymous inner class + */ + Future future = executorService.submit(new Callable() { + @Override + public String call() throws Exception { + return methodToBeMock(); + } + }); + return future.get(); + } + + public String callLambdaInner() throws ExecutionException, InterruptedException { + /** + * invocation inside a lambda inner class + */ + Future future = executorService.submit(() -> methodToBeMock()); + return future.get(); + } + + public String callInnerDemo() { + return new Inner().demo(); + } + + public static String methodToBeMock() { + return "RealCall"; + } + +} diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoInnerClassTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoInnerClassTest.java new file mode 100644 index 0000000..8a363c7 --- /dev/null +++ b/demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoInnerClassTest.java @@ -0,0 +1,29 @@ +package com.alibaba.demo.basic; + +import com.alibaba.testable.core.annotation.MockMethod; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * 演示对内部类的Mock支持 + * Demonstrate support for mocking invocation inside a inner class + */ +class DemoInnerClassTest { + + public static class Mock { + @MockMethod(targetClass = DemoInnerClass.class) + String methodToBeMock() { + return "MockedCall"; + } + } + + @Test + void should_able_to_mock_invoke_inside_inner_class() throws Exception { + DemoInnerClass demo = new DemoInnerClass(); + assertEquals("MockedCall", demo.callInnerDemo()); + assertEquals("MockedCall", demo.callAnonymousInner()); + assertEquals("MockedCall", demo.callLambdaInner()); + assertEquals("MockedCall", new DemoInnerClass.StaticInner().demo()); + } +} diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/demo/basic/DemoInnerClass.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/demo/basic/DemoInnerClass.kt new file mode 100644 index 0000000..cad0329 --- /dev/null +++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/demo/basic/DemoInnerClass.kt @@ -0,0 +1,49 @@ +package com.alibaba.demo.basic + +import java.util.concurrent.ExecutionException +import java.util.concurrent.Executors + +/** + * 演示对内部类的Mock支持 + * Demonstrate support for mocking invocation inside a inner class + */ +class DemoInnerClass { + + class StaticInner { + /** + * invocation inside a static inner class + */ + fun demo(): String { + return methodToBeMock() + } + } + + inner class Inner { + /** + * invocation inside a non-static inner class + */ + fun demo(): String { + return methodToBeMock() + } + } + + private val executorService = Executors.newSingleThreadExecutor() + @Throws(ExecutionException::class, InterruptedException::class) + fun callAnonymousInner(): String { + /** + * invocation inside a anonymous inner class + */ + val future = executorService.submit { methodToBeMock() } + return future.get() + } + + fun callInnerDemo(): String { + return Inner().demo() + } + + companion object { + fun methodToBeMock(): String { + return "RealCall" + } + } +} diff --git a/demo/kotlin-demo/src/test/kotlin/com/alibaba/demo/basic/DemoInnerClassTest.kt b/demo/kotlin-demo/src/test/kotlin/com/alibaba/demo/basic/DemoInnerClassTest.kt new file mode 100644 index 0000000..a4b2150 --- /dev/null +++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/demo/basic/DemoInnerClassTest.kt @@ -0,0 +1,28 @@ +package com.alibaba.demo.basic + +import com.alibaba.testable.core.annotation.MockMethod +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +/** + * 演示对内部类的Mock支持 + * Demonstrate support for mocking invocation inside a inner class + */ +internal class DemoInnerClassTest { + + class Mock { + @MockMethod(targetClass = DemoInnerClass::class) + fun methodToBeMock(): String { + return "MockedCall" + } + } + + @Test + @Throws(Exception::class) + fun should_able_to_mock_invoke_inside_inner_class() { + val demo = DemoInnerClass() + Assertions.assertEquals("MockedCall", demo.callInnerDemo()) + Assertions.assertEquals("MockedCall", demo.callAnonymousInner()) + Assertions.assertEquals("MockedCall", DemoInnerClass.StaticInner().demo()) + } +} 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 a8a992e..f9021aa 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 @@ -111,7 +111,16 @@ public class TestableClassTransformer implements ClassFileTransformer { if (mockClass != null) { return mockClass; } - return foundMockForTestClass(ClassUtil.getTestClassName(className)); + mockClass = foundMockForTestClass(ClassUtil.getTestClassName(className)); + if (mockClass != null) { + return mockClass; + } + return foundMockForInnerSourceClass(className); + } + + private String foundMockForInnerSourceClass(String className) { + return (className.contains(DOLLAR) && !className.endsWith(KOTLIN_POSTFIX_COMPANION)) ? + foundMockForTestClass(ClassUtil.getTestClassName(className.substring(0, className.indexOf(DOLLAR)))) : null; } private String foundMockForTestClass(String className) { diff --git a/testable-agent/src/test/java/com/alibaba/testable/agent/util/MethodUtilTest.java b/testable-agent/src/test/java/com/alibaba/testable/agent/util/MethodUtilTest.java index 9f94240..be4b84a 100644 --- a/testable-agent/src/test/java/com/alibaba/testable/agent/util/MethodUtilTest.java +++ b/testable-agent/src/test/java/com/alibaba/testable/agent/util/MethodUtilTest.java @@ -41,11 +41,11 @@ class MethodUtilTest { @Test void should_able_to_convert_bytecode_parameters() { assertEquals("", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "")); - assertEquals("char", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "C")); - assertEquals("int,long", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "IJ")); - assertEquals("int[],long[]", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "[I[J")); - assertEquals("int,java.lang.String", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "ILjava/lang/String;")); - assertEquals("java.lang.String,int,long[]", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "Ljava/lang/String;I[J")); + assertEquals("void", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "V")); + assertEquals("int, long", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "IJ")); + assertEquals("int[], long[]", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "[I[J")); + assertEquals("int, java.lang.String", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "ILjava/lang/String;")); + assertEquals("java.lang.String, int, long[]", PrivateAccessor.invokeStatic(MethodUtil.class, "toJavaParameterDesc", "Ljava/lang/String;I[J")); } }