From e33c208b8d2108fc48c2b11aa9d6b7bf9ddbb32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Wed, 28 Oct 2020 09:12:25 +0800 Subject: [PATCH] split private access and mock demo class --- .../testable/demo/{ => model}/BlackBox.java | 2 +- .../testable/demo/{ => model}/Box.java | 2 +- .../DemoMockService.java} | 35 +++-------- .../service/DemoPrivateAccessService.java | 25 ++++++++ .../DemoMockServiceTest.java} | 32 +++------- .../service/DemoPrivateAccessServiceTest.java | 29 +++++++++ .../testable/demo/{ => model}/BlackBox.kt | 2 +- .../alibaba/testable/demo/{ => model}/Box.kt | 2 +- .../DemoMockService.kt} | 36 ++++------- .../demo/service/DemoPrivateAccessService.kt | 26 ++++++++ .../testable/demo/{ => util}/PathUtil.kt | 2 +- .../DemoMockServiceTest.kt} | 28 +++------ .../service/DemoPrivateAccessServiceTest.kt | 23 +++++++ .../testable/demo/{ => util}/PathUtilTest.kt | 3 +- docs/usage.md | 62 ++++++++++++++++--- 15 files changed, 202 insertions(+), 107 deletions(-) rename demo/java-demo/src/main/java/com/alibaba/testable/demo/{ => model}/BlackBox.java (89%) rename demo/java-demo/src/main/java/com/alibaba/testable/demo/{ => model}/Box.java (59%) rename demo/java-demo/src/main/java/com/alibaba/testable/demo/{DemoService.java => service/DemoMockService.java} (62%) create mode 100644 demo/java-demo/src/main/java/com/alibaba/testable/demo/service/DemoPrivateAccessService.java rename demo/java-demo/src/test/java/com/alibaba/testable/demo/{DemoServiceTest.java => service/DemoMockServiceTest.java} (74%) create mode 100644 demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java rename demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/{ => model}/BlackBox.kt (94%) rename demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/{ => model}/Box.kt (56%) rename demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/{DemoService.kt => service/DemoMockService.kt} (60%) create mode 100644 demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessService.kt rename demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/{ => util}/PathUtil.kt (94%) rename demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/{DemoServiceTest.kt => service/DemoMockServiceTest.kt} (79%) create mode 100644 demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt rename demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/{ => util}/PathUtilTest.kt (92%) 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/model/BlackBox.java similarity index 89% rename from demo/java-demo/src/main/java/com/alibaba/testable/demo/BlackBox.java rename to demo/java-demo/src/main/java/com/alibaba/testable/demo/model/BlackBox.java index 7201ed0..699a09c 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/model/BlackBox.java @@ -1,4 +1,4 @@ -package com.alibaba.testable.demo; +package com.alibaba.testable.demo.model; public class BlackBox implements Box { 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/model/Box.java similarity index 59% rename from demo/java-demo/src/main/java/com/alibaba/testable/demo/Box.java rename to demo/java-demo/src/main/java/com/alibaba/testable/demo/model/Box.java index 78d5735..2778e72 100644 --- a/demo/java-demo/src/main/java/com/alibaba/testable/demo/Box.java +++ b/demo/java-demo/src/main/java/com/alibaba/testable/demo/model/Box.java @@ -1,4 +1,4 @@ -package com.alibaba.testable.demo; +package com.alibaba.testable.demo.model; public interface Box { 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/service/DemoMockService.java similarity index 62% rename from demo/java-demo/src/main/java/com/alibaba/testable/demo/DemoService.java rename to demo/java-demo/src/main/java/com/alibaba/testable/demo/service/DemoMockService.java index 49efa0f..f7a619f 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/service/DemoMockService.java @@ -1,32 +1,17 @@ -package com.alibaba.testable.demo; +package com.alibaba.testable.demo.service; +import com.alibaba.testable.demo.model.BlackBox; +import com.alibaba.testable.demo.model.Box; import org.springframework.stereotype.Service; import sun.net.www.http.HttpClient; import java.net.URL; @Service -public class DemoService { - - private int count; +public class DemoMockService { /** - * 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 + * method with new operation */ public String newFunc() { BlackBox component = new BlackBox("something"); @@ -34,28 +19,28 @@ public class DemoService { } /** - * Target 4 - method with member method invoke + * method with member method invoke */ public String outerFunc(String s) throws Exception { return "{ \"res\": \"" + innerFunc(s) + "\"}"; } /** - * Target 5 - method with common method invoke + * method with common method invoke */ public String commonFunc() { return "anything".trim() + "__" + "anything".substring(1, 2) + "__" + "abc".startsWith("ab"); } /** - * Target 6 - method with static method invoke + * method with static method invoke */ public BlackBox getBox() { return BlackBox.secretBox(); } /** - * Target 7 - method with override method invoke + * method with override method invoke */ public Box putBox() { Box box = new BlackBox(""); @@ -64,7 +49,7 @@ public class DemoService { } /** - * Target 8 - two methods invoke same private method + * two methods invoke same private method */ public String callerOne() { return callFromDifferentMethod(); diff --git a/demo/java-demo/src/main/java/com/alibaba/testable/demo/service/DemoPrivateAccessService.java b/demo/java-demo/src/main/java/com/alibaba/testable/demo/service/DemoPrivateAccessService.java new file mode 100644 index 0000000..fdd2f2d --- /dev/null +++ b/demo/java-demo/src/main/java/com/alibaba/testable/demo/service/DemoPrivateAccessService.java @@ -0,0 +1,25 @@ +package com.alibaba.testable.demo.service; + +import org.springframework.stereotype.Service; + +@Service +public class DemoPrivateAccessService { + + private int count; + + /** + * private method + */ + private String privateFunc(String s, int i) { + return s + " - " + i; + } + + /** + * method with private field access + */ + public String privateFieldAccessFunc() { + count += 2; + return String.valueOf(count); + } + +} 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/service/DemoMockServiceTest.java similarity index 74% rename from demo/java-demo/src/test/java/com/alibaba/testable/demo/DemoServiceTest.java rename to demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoMockServiceTest.java index 6a63f58..f752b9f 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/service/DemoMockServiceTest.java @@ -1,8 +1,8 @@ -package com.alibaba.testable.demo; +package com.alibaba.testable.demo.service; -import com.alibaba.testable.core.accessor.PrivateAccessor; -import com.alibaba.testable.processor.annotation.EnablePrivateAccess; import com.alibaba.testable.core.annotation.TestableMock; +import com.alibaba.testable.demo.model.BlackBox; +import com.alibaba.testable.demo.model.Box; import org.junit.jupiter.api.Test; import java.util.concurrent.Executors; @@ -10,8 +10,7 @@ import java.util.concurrent.Executors; import static com.alibaba.testable.core.tool.TestableTool.*; import static org.junit.jupiter.api.Assertions.assertEquals; -@EnablePrivateAccess -class DemoServiceTest { +class DemoMockServiceTest { @TestableMock(targetMethod = CONSTRUCTOR) private BlackBox createBlackBox(String text) { @@ -19,7 +18,7 @@ class DemoServiceTest { } @TestableMock - private String innerFunc(DemoService self, String text) { + private String innerFunc(DemoMockService self, String text) { return "mock_" + text; } @@ -39,7 +38,7 @@ class DemoServiceTest { } @TestableMock - private BlackBox secretBox(BlackBox _) { + private BlackBox secretBox(BlackBox ignore) { return new BlackBox("not_secret_box"); } @@ -49,7 +48,7 @@ class DemoServiceTest { } @TestableMock - private String callFromDifferentMethod(DemoService self) { + private String callFromDifferentMethod(DemoMockService self) { if (TEST_CASE.equals("should_able_to_get_test_case_name")) { return "mock_special"; } @@ -59,22 +58,7 @@ class DemoServiceTest { } } - private DemoService demoService = new DemoService(); - - @Test - void should_able_to_mock_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_mock_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")); - } + private DemoMockService demoService = new DemoMockService(); @Test void should_able_to_mock_new_object() throws Exception { diff --git a/demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java b/demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java new file mode 100644 index 0000000..0bb3d7f --- /dev/null +++ b/demo/java-demo/src/test/java/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.java @@ -0,0 +1,29 @@ +package com.alibaba.testable.demo.service; + +import com.alibaba.testable.core.accessor.PrivateAccessor; +import com.alibaba.testable.processor.annotation.EnablePrivateAccess; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@EnablePrivateAccess +class DemoPrivateAccessServiceTest { + + private DemoPrivateAccessService demoService = new DemoPrivateAccessService(); + + @Test + void should_able_to_mock_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_mock_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")); + } + +} 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/model/BlackBox.kt similarity index 94% rename from demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/BlackBox.kt rename to demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/model/BlackBox.kt index 1b9af24..e6be1be 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/model/BlackBox.kt @@ -1,4 +1,4 @@ -package com.alibaba.testable.demo +package com.alibaba.testable.demo.model class BlackBox(private var data: String) : Box { 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/model/Box.kt similarity index 56% rename from demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/Box.kt rename to demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/model/Box.kt index 241e73b..d88da88 100644 --- a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/Box.kt +++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/model/Box.kt @@ -1,4 +1,4 @@ -package com.alibaba.testable.demo +package com.alibaba.testable.demo.model interface Box { 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/service/DemoMockService.kt similarity index 60% rename from demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/DemoService.kt rename to demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/service/DemoMockService.kt index 41363b9..845d568 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/service/DemoMockService.kt @@ -1,46 +1,32 @@ -package com.alibaba.testable.demo +package com.alibaba.testable.demo.service +import com.alibaba.testable.demo.model.BlackBox +import com.alibaba.testable.demo.model.Box +import com.alibaba.testable.demo.model.ColorBox import org.springframework.stereotype.Service import sun.net.www.http.HttpClient import java.net.URL @Service -class DemoService { - - private var count = 0 +class DemoMockService { /** - * 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 + * method with new operation */ fun newFunc(): String { return BlackBox("something").get() } /** - * Target 4 - method with member method invoke + * method with member method invoke */ fun outerFunc(s: String): String { return "{ \"res\": \"" + innerFunc(s) + "\"}" } /** - * Target 5 - method with common method invoke + * method with common method invoke */ fun commonFunc(): String { val box = BlackBox("anything") @@ -48,14 +34,14 @@ class DemoService { } /** - * Target 6 - method with static method invoke + * method with static method invoke */ fun getBox(): BlackBox { return ColorBox.createBox("Red", BlackBox.secretBox()) } /** - * Target 7 - method with override method invoke + * method with override method invoke */ fun putBox(): Box { val box: Box = BlackBox("") @@ -64,7 +50,7 @@ class DemoService { } /** - * Target 8 - two methods invoke same private method + * two methods invoke same private method */ fun callerOne(): String { return callFromDifferentMethod() diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessService.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessService.kt new file mode 100644 index 0000000..5ded99d --- /dev/null +++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessService.kt @@ -0,0 +1,26 @@ +package com.alibaba.testable.demo.service + +import org.springframework.stereotype.Service + + +@Service +class DemoPrivateAccessService { + + private var count = 0 + + /** + * private method + */ + private fun privateFunc(s: String, i: Int): String { + return "$s - $i" + } + + /** + * method with private field access + */ + fun privateFieldAccessFunc(): String { + count += 2 + return count.toString() + } + +} diff --git a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/PathUtil.kt b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/util/PathUtil.kt similarity index 94% rename from demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/PathUtil.kt rename to demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/util/PathUtil.kt index 183c8c1..9e0da2b 100644 --- a/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/PathUtil.kt +++ b/demo/kotlin-demo/src/main/kotlin/com/alibaba/testable/demo/util/PathUtil.kt @@ -1,4 +1,4 @@ -package com.alibaba.testable.demo +package com.alibaba.testable.demo.util import java.io.File import java.io.IOException 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/service/DemoMockServiceTest.kt similarity index 79% rename from demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/DemoServiceTest.kt rename to demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoMockServiceTest.kt index 2677872..f6c10c9 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/service/DemoMockServiceTest.kt @@ -1,22 +1,22 @@ -package com.alibaba.testable.demo +package com.alibaba.testable.demo.service -import com.alibaba.testable.core.accessor.PrivateAccessor import com.alibaba.testable.core.annotation.TestableMock import com.alibaba.testable.core.tool.TestableTool.* -import com.alibaba.testable.processor.annotation.EnablePrivateAccess +import com.alibaba.testable.demo.model.BlackBox +import com.alibaba.testable.demo.model.Box +import com.alibaba.testable.demo.model.ColorBox import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import java.util.concurrent.Executors -@EnablePrivateAccess -internal class DemoServiceTest { +internal class DemoMockServiceTest { @TestableMock(targetMethod = CONSTRUCTOR) private fun createBlackBox(text: String) = BlackBox("mock_$text") @TestableMock - private fun innerFunc(self: DemoService, text: String) = "mock_$text" + private fun innerFunc(self: DemoMockService, text: String) = "mock_$text" @TestableMock private fun trim(self: BlackBox) = "trim_string" @@ -43,7 +43,7 @@ internal class DemoServiceTest { } @TestableMock - private fun callFromDifferentMethod(self: DemoService): String { + private fun callFromDifferentMethod(self: DemoMockService): String { return if (TEST_CASE == "should_able_to_get_test_case_name") { "mock_special" } else { @@ -54,19 +54,7 @@ internal class DemoServiceTest { } } - private val demoService = DemoService() - - @Test - fun should_able_to_mock_private_method() { - assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1)) - } - - @Test - fun should_able_to_mock_private_field() { - PrivateAccessor.set(demoService, "count", 3) - assertEquals("5", demoService.privateFieldAccessFunc()) - assertEquals(5, PrivateAccessor.get(demoService, "count")) - } + private val demoService = DemoMockService() @Test fun should_able_to_mock_new_object() { diff --git a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt new file mode 100644 index 0000000..4192139 --- /dev/null +++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/service/DemoPrivateAccessServiceTest.kt @@ -0,0 +1,23 @@ +package com.alibaba.testable.demo.service + +import com.alibaba.testable.core.accessor.PrivateAccessor +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + + +internal class DemoPrivateAccessServiceTest { + + private val demoService = DemoPrivateAccessService() + + @Test + fun should_able_to_mock_private_method() { + assertEquals("hello - 1", PrivateAccessor.invoke(demoService, "privateFunc", "hello", 1)) + } + + @Test + fun should_able_to_mock_private_field() { + PrivateAccessor.set(demoService, "count", 3) + assertEquals("5", demoService.privateFieldAccessFunc()) + assertEquals(5, PrivateAccessor.get(demoService, "count")) + } +} diff --git a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/PathUtilTest.kt b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/util/PathUtilTest.kt similarity index 92% rename from demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/PathUtilTest.kt rename to demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/util/PathUtilTest.kt index 77b9d2e..fddd26b 100644 --- a/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/PathUtilTest.kt +++ b/demo/kotlin-demo/src/test/kotlin/com/alibaba/testable/demo/util/PathUtilTest.kt @@ -1,8 +1,9 @@ -package com.alibaba.testable.demo +package com.alibaba.testable.demo.util import org.junit.jupiter.api.Test import com.alibaba.testable.core.annotation.TestableMock import com.alibaba.testable.core.tool.TestableTool.verify +import com.alibaba.testable.demo.util.PathUtil import java.io.File class PathUtilTest { diff --git a/docs/usage.md b/docs/usage.md index 439dec6..f8b7d2f 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -51,7 +51,7 @@ 若不希望看到IDE的语法错误提醒,或是在基于JVM的非Java语言项目里(譬如Kotlin语言),也可以借助`PrivateAccessor`工具类来实现私有成员的访问。 -效果见示例项目文件`DemoServiceTest.java`中的`should_able_to_mock_private_method()`和`should_able_to_mock_private_field()`测试用例。 +效果见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_private_method()`和`should_able_to_mock_private_field()`测试用例。 ### Mock被测类的任意方法调用 @@ -61,9 +61,22 @@ 此时被测类中所有对该需覆写方法的调用,将在单元测试运行时,将自动被替换为对上述自定义Mock方法的调用。 -**注意**:当遇到有两个需覆写的方法重名时,可将需覆写的方法名写到`@TestableMock`注解的`targetMethod`参数里,此时Mock方法自身就可以随意命名了。 +**注意**:也可以将需覆写的方法名写到`@TestableMock`注解的`targetMethod`参数里,这样Mock方法自身就可以随意命名了(当遇到重名的待覆写方法时特别有用)。 -示例项目文件`DemoServiceTest.java`中的`should_able_to_mock_common_method()`用例详细展示了这种用法。 +例如,被测类中有一处`"anything".substring(1, 2)`调用,我们希望在运行测试的时候将它换成一个固定字符串,则只需在测试类定义如下方法: + +```java +// 原方法签名为`String substring(int, int)` +// 调用此方法的对象`"anything"`类型为`String` +// 则Mock方法签名在其参数列表首位增加一个类型为`String`的参数(名字随意) +// 此参数可用于获得当时的实际调用者的值和上下文 +@TestableMock +private String substring(String self, int i, int j) { + return "sub_string"; +} +``` + +完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_common_method()`测试用例。(由于Kotlin对String类型进行了魔改,故Kotlin示例中将被测方法在`BlackBox`类里加了一层封装) **2. 覆写被测类自身的成员方法** @@ -71,13 +84,36 @@ 操作方法与前一种情况相同,Mock方法的第一个参数类型需与被测类相同,即可实现对被测类自身(不论是公有或私有)成员方法的覆写。 -详见示例项目文件`DemoServiceTest.java`中的`should_able_to_mock_member_method()`用例。 +例如,被测类中有一个签名为`String innerFunc(String)`的私有方法,我们希望在测试的时候将它替换掉,则只需在测试类定义如下方法: + +```java +// 被测类型是`DemoMockService` +// 因此在定义Mock方法时,在目标方法参数首位加一个类型为`DemoMockService`的参数(名字随意) +@TestableMock +private String innerFunc(DemoMockService self, String text) { + return "mock_" + text; +} +``` + +完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_member_method()`测试用例。 **3. 覆写任意类的静态方法** 对于静态方法的Mock与普通方法相同。但需要注意的是,对于静态方法,传入Mock方法的第一个参数实际值始终是`null`。 -详见示例项目文件`DemoServiceTest.java`中的`should_able_to_mock_static_method()`用例。 +例如,在被测类中调用了`BlackBox`类型中的静态方法`secretBox()`,改方法签名为`BlackBox secretBox()`,则Mock方法如下: + +```java +// 目标静态方法定义在`BlackBox`类型中 +// 在定义Mock方法时,在目标方法参数首位加一个类型为`BlackBox`的参数(名字随意) +// 此参数仅用于标识目标类型,实际传入值将始终为`null` +@TestableMock +private BlackBox secretBox(BlackBox ignore) { + return new BlackBox("not_secret_box"); +} +``` + +完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_static_method()`测试用例。 **4. 覆写任意类的new操作** @@ -85,10 +121,22 @@ 此时被测类中所有用`new`创建指定类的操作(并使用了与Mock方法参数一致的构造函数)将被替换为对该自定义方法的调用。 -详见示例项目文件`DemoServiceTest.java`中的`should_able_to_mock_new_object()`用例。 +例如,在被测类中有一处`new BlackBox("something")`调用,希望在测试时将它换掉(通常是换成Mock对象,或换成使用测试参数创建的临时对象),则只需定义如下Mock方法: + +```java +// 要覆写的构造函数签名为`BlackBox(String)` +// 无需在Mock方法参数列表增加额外参数,由于使用了`targetMethod`参数,Mock方法的名称随意起 +// 此处的`CONSTRUCTOR`为`TestableTool`辅助类提供的常量,值为"" +@TestableMock(targetMethod = CONSTRUCTOR) +private BlackBox createBlackBox(String text) { + return new BlackBox("mock_" + text); +} +``` + +完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_mock_new_object()`测试用例。 **5. 识别当前测试用例和调用来源** 在Mock方法中可以通过`TestableTool.TEST_CASE`和`TestableTool.SOURCE_METHOD`来识别**当前运行的测试用例名称**和**进入该Mock方法前的被测类方法名称**,从而区分处理不同的调用场景。 -详见示例项目文件`DemoServiceTest.java`中的`should_able_to_get_source_method_name()`和`should_able_to_get_test_case_name()`用例。 +完整代码示例见`java-demo`和`kotlin-demo`示例项目中的`should_able_to_get_source_method_name()`和`should_able_to_get_test_case_name()`测试用例。