add kotlin demo for mock scope

This commit is contained in:
金戟 2021-05-01 17:25:48 +08:00
parent b68ff99800
commit 0e581a7eeb
7 changed files with 111 additions and 9 deletions

View File

@ -7,19 +7,19 @@ package com.alibaba.demo.association;
public class CookerService {
private static String hireSandwichCooker() {
return "Sandwich-Cooker";
return "Real-Sandwich-Cooker";
}
private static String hireHamburgerCooker() {
return "Hamburger-Cooker";
return "Real-Hamburger-Cooker";
}
private String cookSandwich() {
return "Cooked-Sandwich";
return "Real-Sandwich";
}
private String cookHamburger() {
return "Cooked-Hamburger";
return "Real-Hamburger";
}
public String prepareSandwich() {

View File

@ -15,7 +15,7 @@ class SellerServiceTest {
@Test
void should_sell_hamburger() {
assertEquals("Hamburger-Cooker & Cooked-Hamburger", sellerService.sellHamburger());
assertEquals("Real-Hamburger-Cooker & Real-Hamburger", sellerService.sellHamburger());
}
}

View File

@ -0,0 +1,34 @@
package com.alibaba.demo.association
/**
* 目标类此类中的一些调用将会被Mock掉
* Target class, some invocations inside this class will be mocked
*/
class CookerService {
private fun cookSandwich(): String {
return "Real-Sandwich"
}
private fun cookHamburger(): String {
return "Real-Hamburger"
}
fun prepareSandwich(): String {
return hireSandwichCooker() + " & " + cookSandwich()
}
fun prepareHamburger(): String {
return hireHamburgerCooker() + " & " + cookHamburger()
}
companion object {
private fun hireSandwichCooker(): String {
return "Real-Sandwich-Cooker"
}
private fun hireHamburgerCooker(): String {
return "Real-Hamburger-Cooker"
}
}
}

View File

@ -0,0 +1,18 @@
package com.alibaba.demo.association
/**
* 被测类会访问`CookerService`里的方法
* Class to be tested, which will access methods in TargetService class
*/
class SellerService {
private val cookerService = CookerService()
fun sellSandwich(): String {
return cookerService.prepareSandwich()
}
fun sellHamburger(): String {
return cookerService.prepareHamburger()
}
}

View File

@ -0,0 +1,27 @@
package com.alibaba.demo.association
import com.alibaba.testable.core.annotation.MockMethod
import com.alibaba.testable.core.model.MockScope
internal class CookerServiceMock {
@MockMethod(targetClass = CookerService::class)
private fun cookSandwich(): String {
return "Faked-Sandwich"
}
@MockMethod(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
private fun cookHamburger(): String {
return "Faked-Hamburger"
}
@MockMethod(targetClass = CookerService::class)
fun hireSandwichCooker(): String {
return "Fake-Sandwich-Cooker"
}
@MockMethod(targetClass = CookerService::class, scope = MockScope.ASSOCIATED)
fun hireHamburgerCooker(): String {
return "Fake-Hamburger-Cooker"
}
}

View File

@ -0,0 +1,19 @@
package com.alibaba.demo.association
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
internal class SellerServiceTest {
private val sellerService = SellerService()
@Test
fun should_sell_sandwich() {
Assertions.assertEquals("Fake-Sandwich-Cooker & Faked-Sandwich", sellerService.sellSandwich())
}
@Test
fun should_sell_hamburger() {
Assertions.assertEquals("Real-Hamburger-Cooker & Real-Hamburger", sellerService.sellHamburger())
}
}

View File

@ -6,11 +6,15 @@ Mock的生效范围
- `MockScope.GLOBAL`该Mock方法将全局生效
- `MockScope.ASSOCIATED`该Mock方法仅对Mock容器关联测试类中的测试用例生效
举例来说,`AaaService`和`BbbService`是两个需要被测试的类,在`BbbService`的代码里有个`recordTicket()`调用依赖外部系统。因此在进行单元测试时,开发者在`BbbService`关联的Mock容器里使用`@MockMethod`注解定义了这个调用的替代方法。此时若该Mock方法的`scope`值为`MockScope.GLOBAL`,则不论是在`AaaServiceTest`测试类还是在`BbbServiceTest`测试类的测试用例只要直接或间接的执行到这行调用都会被置换为调用Mock方法。若该Mock方法的`scope`值为`MockScope.ASSOCIATED`则Mock只对`BbbServiceTest`类中的测试用例生效,而`AaaServiceTest`类中的测试用例在运行过程中执行到了`BbbService`类的相关代码,将会执行`recordTicket()`的原本调用。
对于常规项目而言单元测试里需要被Mock的调用都是由于其中包含了不需要或不便于测试的逻辑譬如“依赖外部系统”、“包含随机结果”、“执行非常耗时”等等这类调用在整个单元测试的生命周期里都应该被Mock方法置换不论调用的发起者是谁。因此`TestableMock`默认所有Mock方法都是全局生效的即`scope`默认值为`MockScope.GLOBAL`。
在一些大型项目中会有“下层模块编写单元测试上层模块编写端到端集成测试两者混合在一起运行”的情况这时候大部分Mock方法都应该使用`MockScope.ASSOCIATED`作为生效范围。针对这种情况,`TestableMock`支持通过`mockScope`运行参数来修改默认的Mock方法生效范围详见[全局运行参数](zh-cn/doc/javaagent-args.md)文档。
> 举例来说,`CookerService`和`SellerService`是两个需要被测试的类,假设`CookerService`的代码里的`hireXxx()`和`cookXxx()`方法都需要依赖外部系统。因此在进行单元测试时,开发者在`CookerService`关联的Mock容器里使用`@MockMethod`注解定义了这些调用的替代方法。
>
> 此时若该Mock方法的`scope`值为`MockScope.GLOBAL`,则不论是在`SellerServiceTest`测试类还是在`CookerServiceTest`测试类的测试用例只要直接或间接的执行到这行调用都会被置换为调用Mock方法。若该Mock方法的`scope`值为`MockScope.ASSOCIATED`则Mock只对`CookerServiceTest`类中的测试用例生效,而`SellerServiceTest`类中的测试用例在运行过程中执行到了`CookerService`类的相关代码,将会执行原本的调用。
>
> 参见Java和Kotlin示例中`SellerServiceTest`测试类的用例。
在一些大型项目中会有“下层模块编写单元测试上层模块编写端到端集成测试两者混合在一起运行”的情况这时候大部分Mock方法都应该使用`MockScope.ASSOCIATED`作为生效范围。针对这种情况,`TestableMock`支持通过`mock.scope.default`运行参数来修改默认的Mock方法生效范围详见[全局运行参数](zh-cn/doc/javaagent-args.md)文档。
> 特别说明。若要Mock静态块里的调用Mock方法的`scope`必须为`MockScope.GLOBAL`因为静态块中的代码在程序初始化时就会执行不属于任何测试用例。典型场景是在使用JNI开发的项目中Mock系统库的加载方法。
> ```java
@ -24,4 +28,4 @@ Mock的生效范围
> private void loadLibrary(String libname) {
> System.err.println("loadLibrary " + libname);
> }
> ```
> ```