From ae134ac5ce2b9673be8df7e7918bb7da6d024086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Tue, 27 Oct 2020 20:00:25 +0800 Subject: [PATCH] support mock invoke by interface instance --- .../java/com/alibaba/testable/demo/BlackBox.java | 9 +++++++-- .../main/java/com/alibaba/testable/demo/Box.java | 7 +++++++ .../com/alibaba/testable/demo/DemoService.java | 14 +++++++++++++- .../alibaba/testable/demo/DemoServiceTest.java | 14 +++++++++++++- .../kotlin/com/alibaba/testable/demo/BlackBox.kt | 10 +++++++--- .../main/kotlin/com/alibaba/testable/demo/Box.kt | 7 +++++++ .../com/alibaba/testable/demo/DemoService.kt | 14 +++++++++++++- .../com/alibaba/testable/demo/DemoServiceTest.kt | 16 ++++++++++++++-- .../agent/handler/SourceClassHandler.java | 11 ++++++----- 9 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 demo/java-demo/src/main/java/com/alibaba/testable/demo/Box.java create mode 100644 demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/Box.kt 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 index 025bb15..7201ed0 100644 --- 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 @@ -1,14 +1,19 @@ package com.alibaba.testable.demo; -public class BlackBox { +public class BlackBox implements Box { private String data; + @Override + public void put(String something) { + data = something; + } + public BlackBox(String data) { this.data = data; } - public String callMe() { + public String get() { return data; } diff --git a/demo/java-demo/src/main/java/com/alibaba/testable/demo/Box.java b/demo/java-demo/src/main/java/com/alibaba/testable/demo/Box.java new file mode 100644 index 0000000..78d5735 --- /dev/null +++ b/demo/java-demo/src/main/java/com/alibaba/testable/demo/Box.java @@ -0,0 +1,7 @@ +package com.alibaba.testable.demo; + +public interface Box { + + void put(String something); + +} 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 index 5b0f593..49efa0f 100644 --- 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 @@ -30,7 +30,7 @@ public class DemoService { */ public String newFunc() { BlackBox component = new BlackBox("something"); - return component.callMe(); + return component.get(); } /** @@ -54,6 +54,18 @@ public class DemoService { return BlackBox.secretBox(); } + /** + * Target 7 - method with override method invoke + */ + public Box putBox() { + Box box = new BlackBox(""); + box.put("data"); + return box; + } + + /** + * Target 8 - two methods invoke same private method + */ public String callerOne() { return callFromDifferentMethod(); } 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 50a8b7f..6a63f58 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 @@ -43,6 +43,11 @@ class DemoServiceTest { return new BlackBox("not_secret_box"); } + @TestableMock + private void put(Box self, String something) { + self.put("put_" + something + "_mocked"); + } + @TestableMock private String callFromDifferentMethod(DemoService self) { if (TEST_CASE.equals("should_able_to_get_test_case_name")) { @@ -93,10 +98,17 @@ class DemoServiceTest { @Test void should_able_to_mock_static_method() throws Exception { - assertEquals("not_secret_box", demoService.getBox().callMe()); + assertEquals("not_secret_box", demoService.getBox().get()); verify("secretBox").times(1); } + @Test + void should_able_to_mock_override_method() throws Exception { + BlackBox box = (BlackBox)demoService.putBox(); + verify("put").times(1); + assertEquals("put_data_mocked", box.get()); + } + @Test void should_able_to_get_source_method_name() throws Exception { // synchronous 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 index a789a3c..1b9af24 100644 --- 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 @@ -1,9 +1,13 @@ package com.alibaba.testable.demo -class BlackBox(private val data: String) { +class BlackBox(private var data: String) : Box { - fun callMe(): String { + override fun put(something: String) { + data = something + } + + fun get(): String { return data } @@ -29,6 +33,6 @@ class BlackBox(private val data: String) { object ColorBox { fun createBox(color: String, box: BlackBox): BlackBox { - return BlackBox("${color}_${box.callMe()}") + return BlackBox("${color}_${box.get()}") } } diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/Box.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/Box.kt new file mode 100644 index 0000000..241e73b --- /dev/null +++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/Box.kt @@ -0,0 +1,7 @@ +package com.alibaba.testable.demo + +interface Box { + + fun put(something: String) + +} 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 index bf39b8d..41363b9 100644 --- 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 @@ -29,7 +29,7 @@ class DemoService { * Target 3 - method with new operation */ fun newFunc(): String { - return BlackBox("something").callMe() + return BlackBox("something").get() } /** @@ -54,6 +54,18 @@ class DemoService { return ColorBox.createBox("Red", BlackBox.secretBox()) } + /** + * Target 7 - method with override method invoke + */ + fun putBox(): Box { + val box: Box = BlackBox("") + box.put("data") + return box + } + + /** + * Target 8 - two methods invoke same private method + */ fun callerOne(): String { return callFromDifferentMethod() } 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 ce01a0f..2677872 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 @@ -34,7 +34,12 @@ internal class DemoServiceTest { @TestableMock private fun createBox(ignore: ColorBox, color: String, box: BlackBox): BlackBox { - return BlackBox("White_${box.callMe()}") + return BlackBox("White_${box.get()}") + } + + @TestableMock + private fun put(self: Box, something: String) { + self.put("put_" + something + "_mocked") } @TestableMock @@ -85,11 +90,18 @@ internal class DemoServiceTest { @Test fun should_able_to_mock_static_method() { - assertEquals("White_not_secret_box", demoService.getBox().callMe()) + assertEquals("White_not_secret_box", demoService.getBox().get()) verify("secretBox").times(1) verify("createBox").times(1) } + @Test + fun should_able_to_mock_override_method() { + val box = demoService.putBox() as BlackBox + verify("put").times(1) + assertEquals("put_data_mocked", box.get()) + } + @Test fun should_able_to_get_source_method_name() { // synchronous 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 875b149..b00119e 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 @@ -19,9 +19,10 @@ public class SourceClassHandler extends BaseClassHandler { private final List injectMethods; private final Set invokeOps = new HashSet() {{ - add(Opcodes.INVOKESTATIC); - add(Opcodes.INVOKESPECIAL); add(Opcodes.INVOKEVIRTUAL); + add(Opcodes.INVOKESPECIAL); + add(Opcodes.INVOKESTATIC); + add(Opcodes.INVOKEINTERFACE); }}; public SourceClassHandler(List injectMethods) { @@ -58,7 +59,7 @@ public class SourceClassHandler extends BaseClassHandler { MethodInsnNode node = (MethodInsnNode)instructions[i]; String memberInjectMethodName = getMemberInjectMethodName(memberInjectMethodList, node); if (memberInjectMethodName != null) { - // it's a member method and an inject method for it exist + // it's a member or static method and an inject method for it exist int rangeStart = getMemberMethodStart(instructions, i); if (rangeStart >= 0) { instructions = replaceMemberCallOps(cn, mn, memberInjectMethodName, instructions, @@ -119,11 +120,11 @@ public class SourceClassHandler extends BaseClassHandler { int stackLevel = ClassUtil.getParameterTypes(((MethodInsnNode)instructions[rangeEnd]).desc).size(); for (int i = rangeEnd - 1; i >= 0; i--) { switch (instructions[i].getOpcode()) { - case Opcodes.INVOKEINTERFACE: case Opcodes.INVOKEVIRTUAL: case Opcodes.INVOKESPECIAL: - case Opcodes.INVOKEDYNAMIC: case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEINTERFACE: + case Opcodes.INVOKEDYNAMIC: stackLevel += ClassUtil.getParameterTypes(((MethodInsnNode)instructions[i]).desc).size(); break; case -1: