目录
在@MockMethod
和@MockConstructor
注解上都有一个scope
参数,其可选值有两种:
MockScope.GLOBAL
:该Mock方法将全局生效;MockScope.ASSOCIATED
:该Mock方法仅对Mock容器关联测试类中的测试用例生效;对于常规项目而言,单元测试里需要被Mock的调用都是由于其中包含了不需要或不便于测试的逻辑,譬如“依赖外部系统”、“包含随机结果”、“执行非常耗时”等等,这类调用在整个单元测试的生命周期里都应该被Mock方法置换,不论调用的发起者是谁。因此TestableMock
默认所有Mock方法都是全局生效的,即scope
默认值为MockScope.GLOBAL
。
举例来说,
CookerService
和SellerService
是两个需要被测试的类,假设CookerService
的代码里的hireXxx()
和cookXxx()
方法都需要依赖外部系统。因此在进行单元测试时,开发者在CookerService
关联的Mock容器里使用@MockMethod
注解定义了这些调用的替代方法。此时,若该Mock方法的
scope
值为MockScope.GLOBAL
,则不论是在SellerServiceTest
测试类还是在CookerServiceTest
测试类的测试用例,只要直接或间接的执行到这行调用,都会被置换为调用Mock方法。若该Mock方法的scope
值为MockScope.ASSOCIATED
,则Mock只对CookerServiceTest
类中的测试用例生效,而SellerServiceTest
类中的测试用例在运行过程中执行到了CookerService
类的相关代码,将会执行原本的调用。
【a】编写CookerService被测试类
package com.wsh.testable.mock.testablemockdemo;
/**
* 目标类,此类中的一些调用将会被Mock掉
*/
public class CookerService {
private static String hireSandwichCooker() {
return "Real-Sandwich-Cooker";
}
private static String hireHamburgerCooker() {
return "Real-Hamburger-Cooker";
}
private String cookSandwich() {
return "Real-Sandwich";
}
private String cookHamburger() {
return "Real-Hamburger";
}
public String prepareSandwich() {
return hireSandwichCooker() + " & " + cookSandwich();
}
public String prepareHamburger() {
return hireHamburgerCooker() + " & " + cookHamburger();
}
}
【b】编写SellerService被测试类
package com.wsh.testable.mock.testablemockdemo;
/**
* 被测类,会访问`CookerService`里的方法
*/
public class SellerService {
private CookerService cookerService = new CookerService();
public String sellSandwich() {
return cookerService.prepareSandwich();
}
public String sellHamburger() {
return cookerService.prepareHamburger();
}
}
【c】编写针对CookerService被测试类的Mock容器CookerServiceMock
package com.wsh.testable.mock.testablemockdemo;
import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.model.MockScope;
/**
* 此Mock容器类针对CookerService被测试类
*/
class CookerServiceMock {
/**
* 默认scope为MockScope.GLOBAL,全局有效,所以CookerService、SellerService只要
* 有外部调用到此Mock方法,都将执行Mock之后的方法
*/
@MockMethod(targetClass = CookerService.class)
public static String hireSandwichCooker() {
return "Fake-Sandwich-Cooker";
}
/**
* 作用域为MockScope.ASSOCIATED, 则Mock只对CookerServiceTest类中的测试用例生效,而SellerServiceTest类中的测试用例在
* 运行过程中执行到了CookerService类的相关代码,将会执行原本的调用。
*/
@MockMethod(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
public static String hireHamburgerCooker() {
return "Fake-Hamburger-Cooker";
}
@MockMethod(targetClass = CookerService.class)
private String cookSandwich() {
return "Faked-Sandwich";
}
@MockMethod(targetClass = CookerService.class, scope = MockScope.ASSOCIATED)
private String cookHamburger() {
return "Faked-Hamburger";
}
}
可以看到,在Mock容器类中,我们在MockMethod注解上标注了此Mock方法的作用域。如果标注了MockScope.ASSOCIATED,Mock只对CookerServiceTest类中的测试用例生效,而 SellerServiceTest类中的测试用例在运行过程中执行到了CookerService类的相关代码,将会执行原本的调用。
【d】编写CookerServiceTest测试类
package com.wsh.testable.mock.testablemockdemo;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CookerServiceTest {
private CookerService cookerService = new CookerService();
@Test
void should_cooker_sandwich() {
assertEquals("Fake-Sandwich-Cooker & Faked-Sandwich",
cookerService.prepareSandwich());
}
/**
* 因为作用域为:MockScope.ASSOCIATED,Mock只对CookerServiceTest类中的测试用例生效,而
* SellerServiceTest类中的测试用例在运行过程中执行到了CookerService类的相关代码,将会执行原本的调用。
*/
@Test
void should_cooker_hamburger() {
assertEquals("Fake-Hamburger-Cooker & Faked-Hamburger",
cookerService.prepareHamburger());
}
}
【e】编写SellerServiceTest测试类
package com.wsh.testable.mock.testablemockdemo;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class SellerServiceTest {
private SellerService sellerService = new SellerService();
@Test
void should_sell_sandwich() {
assertEquals("Fake-Sandwich-Cooker & Faked-Sandwich", sellerService.sellSandwich());
}
/**
* 因为作用域为:MockScope.ASSOCIATED,Mock只对CookerServiceTest类中的测试用例生效,而
* SellerServiceTest类中的测试用例在运行过程中执行到了CookerService类的相关代码,将会执行原本的调用。
* <p>
* 原本调用就是分别返回: "Real-Hamburger-Cooker"、"Real-Hamburger"。
*/
@Test
void should_sell_hamburger() {
assertEquals("Real-Hamburger-Cooker & Real-Hamburger", sellerService.sellHamburger());
}
}