目录
一、概述
“Don't Repeat Yourself”是软件开发过程当中的一项重要原则(即“DRY原则”),在编写测试代码时,有些通用的基础功能调用语句常常出现在许多相似的业务类里,若测试中需要Mock这些调用,就要在各个测试类中重复提供同样的Mock方法。而通过Mock方法的复用机制,能够很好的避免编写臃肿重复Mock代码的麻烦。
TestableMock支持两种粒度的Mock复用方式:复用Mock类和复用Mock方法。
二、复用Mock类
如果有两个或以上测试类需要Mock的方法近乎相同,那么采用类级别的Mock复用就是最省心的一种方式。
进行类级别的Mock复用,只需将Mock容器定义为独立的类,然后在要使用它的测试类上通过@MockWith
进行引用。例如:
@MockWith(ServiceMock.class)
public class AaaServiceTest {
...
}
@MockWith(ServiceMock.class)
public class BbbServiceTest {
...
}
public class ServiceMock {
...
}
这样在AaaServiceTest
和BbbServiceTest
类中的测试用例在执行时,都会用ServiceMock
容器类中定义的Mock方法进行调用匹配和Mock替换。
下面我们通过一个简单的示例,说明如何复用Mock类。
【a】编写TestService,模拟服务提供者提供接口给其他消费者使用
package com.alibaba.demo.basic;
public class TestService {
public String test01() {
return "TestService#test01";
}
public String test02() {
return "TestService#test02";
}
public String test() {
return test01() + " & " + test02();
}
}
【b】编写两个服务消费者,调用服务提供者TestService的接口
package com.alibaba.demo.basic;
public class AaaService {
private TestService testService = new TestService();
public String test01() {
return testService.test();
}
}
package com.alibaba.demo.basic;
public class BbbService {
private TestService testService = new TestService();
public String test01() {
return testService.test();
}
}
【c】编写针对TestService的Mock容器类
使用注解@MockWith(TestServiceMock.class)关联抽取出来的公共Mock类。
package com.alibaba.demo.basic;
import com.alibaba.testable.core.annotation.MockMethod;
/**
* 针对TestService的Mock容器类
*/
class TestServiceMock {
@MockMethod(targetClass = TestService.class)
public String test01() {
return "ServiceMock#Mock#test01";
}
@MockMethod(targetClass = TestService.class)
public String test02() {
return "ServiceMock#Mock#test02";
}
}
【d】编写测试类AaaServiceTest
同样的,使用注解@MockWith(TestServiceMock.class)关联抽取出来的公共Mock类。
package com.alibaba.demo.basic;
import com.alibaba.testable.core.annotation.MockWith;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@MockWith(TestServiceMock.class)
public class AaaServiceTest {
private AaaService aaaService = new AaaService();
@Test
public void test01() {
String result = aaaService.test01();
System.out.println("result = " + result);
Assertions.assertEquals("ServiceMock#Mock#test01 & ServiceMock#Mock#test02", result);
}
}
【e】编写测试类BbbServiceTest
package com.alibaba.demo.basic;
import com.alibaba.testable.core.annotation.MockWith;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@MockWith(TestServiceMock.class)
public class BbbServiceTest {
private BbbService bbbService = new BbbService();
@Test
public void test01() {
String result = bbbService.test01();
System.out.println("result = " + result);
Assertions.assertEquals("ServiceMock#Mock#test01 & ServiceMock#Mock#test02", result);
}
}
【f】运行单元测试
可以看到,单元测试是成功通过的,以上就是关于如何通过抽取公共的Mock来实现Mock复用。
三、复用Mock方法
实际场景中,相比一次性复用整个Mock类的情况,更常见的是对部分高频Mock方法进行复用。
Mock方法的复用可以通过Mock容器类的继承来实现,父类中定义的所有Mock方法都会在子类中自然存在,例如:
public class AaaServiceTest {
public static class Mock extends BasicMock {
...
}
...
}
public class BbbServiceTest {
public static class Mock extends BasicMock {
...
}
...
}
public class BasicMock {
@MockMethod(targetClass = UserDao.class)
protected String getById(int id) {
...
}
}
则名为getById
的Mock方法在AaaServiceTest
和BbbServiceTest
的测试用例执行时都会生效。
同样通过一个示例看下如何通过Mock类的继承实现Mock方法的复用。
【a】编写服务提供者接口
package com.wsh.testable.mock.testablemockdemo;
public class UserDao {
protected String getById(int id) {
return String.valueOf(id);
}
public String test(int id) {
return getById(id);
}
}
【b】编写两个服务消费者,调用服务提供者UserDao的接口
package com.wsh.testable.mock.testablemockdemo;
public class AService {
private UserDao userDao = new UserDao();
public String test(int id) {
return userDao.test(id);
}
}
package com.wsh.testable.mock.testablemockdemo;
public class BService {
private UserDao userDao = new UserDao();
public String test(int id) {
return userDao.test(id);
}
}
【c】编写Mock容器基类
package com.wsh.testable.mock.testablemockdemo;
import com.alibaba.testable.core.annotation.MockMethod;
public class UserDaoMock {
@MockMethod(targetClass = UserDao.class)
protected String getById(int id) {
return "Hello," + id;
}
}
【d】编写测试类AServiceTest
Mock静态内存容器类,通过继承Mock基类实现Mock方法复用。
package com.wsh.testable.mock.testablemockdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class AServiceTest {
private AService aService = new AService();
public static class Mock extends UserDaoMock {
}
@Test
public void test() {
String result = aService.test(123456);
System.out.println("result = " + result);
Assertions.assertEquals("Hello,123456", result);
}
}
【e】编写测试类BServiceTest
Mock静态内存容器类,通过继承Mock基类实现Mock方法复用。
package com.wsh.testable.mock.testablemockdemo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class BServiceTest {
private BService bService = new BService();
public static class Mock extends UserDaoMock {
}
@Test
public void test() {
String result = bService.test(789);
System.out.println("result = " + result);
Assertions.assertEquals("Hello,789", result);
}
}
【f】运行单元测试
可以看到,我们编写的单元测试用例都是可以成功跑通的。以上就是关于通过继承Mock基类实现Mock方法复用的案例,实际工作中,如果有Mock方法大体类似的场景,我们灵活使用其中一种方式即可。