Mock测试
在测试一个对象A的时候,我们构造一些假的对象来模拟与A之间的交互,而这些对象的行为是我们事先设定符合预期的,通过这些对象来测试A在正常逻辑,异常逻辑或者压力情况下是否工作正常。我们称这种预先设定符合预期的对象为Mock对象,而采用这种方法进行的测试为Mock测试。
为什么Mock
不论是前后端分离,还是系统之间的依赖,又或者是单元测试,为了能够更好的在开发过程中降低各个环节之间的开发耦合,与单元测试执行成功条件复杂度,我们采用Mock的方式进行测试。
什么情况下使用Mock
- 前后端分离 ,前后端分离开发中前端开发需要调用后端的接口,可是后端的代码逻辑还没有完成;
- 系统间数据交互 ,开发过程中我们会与其他的系统使用接口进行数据交互,可是在我们开发的过程中其他系统的接口可能还没有开发完成或者有重大的bug,为了验证我们的代码是否可用我们可以使用Mock进行数据伪造进行测试;
- 单元测试 ,如果我们不适用mock的方式进行单元测试,我们每次运行单元测试的时候倒要为单元测试搭建一整套的环境单元测试才能正常运行,可是我们只是需要知道进行单元测试的类或方法是否实现我们需要的功能,搭建一整套是如此繁琐且没有任何实际的意义;
- 功能测试 ,当我们进行集成测试的时候,我们只需要保证我们的系统与其他系统的交互是使用的事先定义好的接口格式进行的请求我们就可以在集成测试的时候抛开其他系统只需要测试我们自己的系统。而不需要像系统测试一样搭建一整套的测试环境去进行测试,环境搭建上更快更好进行维护。;
- 接口测试 ,当所在团队使用敏捷开发模式的时候,我们接口测试人员不能等着接口开发完了之后在进行脚本的调试,这时我们就可以按照接口的规则进行Mock搭建,与开发同步进行脚本的开发与调试。;
- 性能测试 ,性能测试中A系统需要与B系统进行交互,A系统只需要知道B系统对数据的处理结果就可以根本不关注B系统是如何处理的,占用了B系统服务器多少的性能指标,因此当我们对A系统进行系统测试的时候,我们只需要MockB系统,给出B系统的处理结果,且Mock服务不要成为性能瓶颈就可以抛开B系统进行A系统的性能测试,这样我们不需要像真实环境中一样需要给出B系统一个配置很高的服务器就可以完成对A系统的性能测试;
怎么用Mock进行测试
我们使用easymock来帮助我们进行mock测试。
当我们进行单元测试的时候我们引入easymock的jar包,并且由easymock帮我们进行对象的mock。来验证我们的方法是否正确的编写。
当我们进行性能测试或者接口测试的时候我们需要使用easymock搭建一个mockserver来为我们提供服务,以此来屏蔽后端或者其他系统。
使用easyMock进行单元测试
我们在pom文件中引入easymock的依赖,以便我们可以使用easymock
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.2</version>
<scope>test</scope>
</dependency>
easymock 可以创建mock对象
marketMock= EasyMock.createMock(StockMarket.class);
EasyMock.expect(marketMock.getPrice("EBAY")).andReturn(42.00);
EasyMock.replay(marketMock);
EasyMock.createMock可以帮助我们创建一个mock对象。mock出来的对象名称为marketMock,被mock的类为StockMarket。
EasyMock.expect().addReturn()可以帮我们对mock的对象进行数据返回模拟。
EasyMock.replay()可以帮我们重放mock这样我们设置的mock数据才能生效才能被使用。
下面来看一个实际例子
Product类来存储商品名称和数量
package com.hik.mockT;
public class Product {
private String name;
private int quantity;
public Product(String name,int quantity){
this.name = name;
this.quantity = quantity;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
Portfolio 类来保存商品列表,Portfolio 类有一个方法用来计算商品的总价格。
package com.hik.mockT;
import java.util.ArrayList;
import java.util.List;
public class Portfolio {
private String name;
private ProductMarket productMarket;
private List<Product> products = new ArrayList<Product>();
public Double getTotalValue() {
Double value = 0.0;
for(Product product:this.products){
value += (productMarket.getPrice(product.getName()) * product.getQuantity());
}
return value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProductMarket getProductMarket() {
return productMarket;
}
public void setProductMarket(ProductMarket productMarket) {
this.productMarket = productMarket;
}
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
public void addProduct(Product product){
products.add(product);
}
}
商品市场接口,提供了一个接口用来查询一个商品的价格
package com.hik.mockT;
public interface ProductMarket {
public Double getPrice(String productName);
}
测试代码
package com.hik.mockT;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import junit.framework.TestCase;
public class PortfolioTest extends TestCase{
private Portfolio portfolio;
private ProductMarket marketMock;
@Before
public void setUp(){
portfolio = new Portfolio();
portfolio.setName("Veera's portfolio.");
marketMock = EasyMock.createMock(ProductMarket.class);
portfolio.setProductMarket(marketMock);
}
@Test
public void testGetTotalvalue() {
EasyMock.expect(marketMock.getPrice("EBAY")).andReturn(42.00);
EasyMock.replay(marketMock);
Product ebayProduct = new Product("EBAY", 2);
portfolio.addProduct(ebayProduct);
assertEquals(84.00, portfolio.getTotalValue());
}
}
EasyMock怎么进行异常情况的测试呢
定义一个接口CalculatorService,提供各种计算功能
package com.hik.mockT;
public interface CalculatorService {
public double add(double input1,double input2);
public double substract(double input1,double input2);
public double multipy(double input1,double input2);
public double divide(double input1,double input2);
}
创建一个类用来使用提供出的接口我们叫他MathApplication
package com.hik.mockT;
public class MathApplication {
private CalculatorService calculatorService;
public void setCalculatorService(CalculatorService calculatorService){
this.calculatorService = calculatorService;
}
public double add(double input1,double input2){
return calculatorService.add(input1, input2);
}
public double substract(double input1,double input2){
return calculatorService.substract(input1, input2);
}
public double multipy(double input1,double input2){
return calculatorService.multipy(input1, input2);
}
public double divide(double input1,double input2){
return calculatorService.divide(input1, input2);
}
}
然后我们开始写测试类用来模拟当发生异常的时候easyMock是怎么处理的
package com.hik.mockT;
import org.easymock.EasyMock;
import org.easymock.EasyMockRunner;
import org.easymock.Mock;
import org.easymock.TestSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import junit.framework.Assert;
//RunWith 测试类的时候初始化测试数据
@RunWith(EasyMockRunner.class)
public class MathApplicationTester {
//声明使用哪个类使用mock对象
@TestSubject
MathApplication mathApplication = new MathApplication();
//声明哪个mock对象将被注入
@Mock
CalculatorService calculatorService;
//当测试预期有异常时,声明期望会有运行时异常
@Test(expected = RuntimeException.class)
public void testAdd(){
//增加mock抛出异常
EasyMock.expect(calculatorService.add(10.0, 20.0)).andThrow(new RuntimeException("加法没有实现"));
//激活mock
EasyMock.replay(calculatorService);
//测试方法
Assert.assertEquals(mathApplication.add(10.0, 20.0), 30.0);
//判断calcService调用 是否被创建
EasyMock.verify(calculatorService);
}
}
当我们不使用mock的时候我们只需要将EasyMock.replay()给注释掉就可以了
MockServer的搭建
待续…