mock框架分享

什么是单元测试?

单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

一、mock框架功能对比:

mock框架分享_第1张图片

二、mock框架简单介绍

EasyMock:

EasyMock 是早期比较流行的MocK测试框架。它提供对接口的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。

使用EasyMock大致可以划分为以下几个步骤:

①    使用 EasyMock 生成 Mock 对象;

②    录制 Mock 对象的预期行为和输出;

③    将 Mock 对象切换到 播放 状态;

④    调用 Mock 对象方法进行单元测试;

⑤    对 Mock 对象的行为进行验证。

@Test

public void testMockMethod() {

Class1Mocked obj= createMock(Class1Mocked.class);

① expect(obj.hello("z3")).andReturn("hello l4");

② replay(obj);

③ String actual= obj.hello("z3");

④ assertEquals("hello l4", actual);

verify(obj);⑤ }

mockito:

使用mockito大致可以划分为以下几个步骤:

①    使用 mockito 生成 Mock 对象;

②    定义(并非录制) Mock 对象的行为和输出(expectations部分);

③    调用 Mock 对象方法进行单元测试;

④    对 Mock 对象的行为进行验证。

@Test

public void testMockMethodInOrder() {

Class1Mocked objOther= mock(Class1Mocked.class);

Class1Mocked objCn= mock(Class1Mocked.class);

when(objOther.hello("z3")).thenReturn("hello l4");

when(objCn.hello("z3")).thenReturn("hello 张三");

String other= objOther.hello("z3");

assertEquals("hello l4", other);

String cn= objCn.hello("z3");

assertEquals("hello 张三", cn);

InOrder inOrder= inOrder(objOther, objCn);//此行并不决定顺序,下面的两行才开始验证顺序inOrder.verify(objOther).hello("z3"); inOrder.verify(objCn).hello("z3"); }

mockito的spy()方式模拟中expectations部分使用的语法不同

when(obj.hello("z3")).thenReturn("hello l4"); 会调用原方法 只是将返回值替换了

doReturn("hello l4").when(obj).hello("z3");

PowerMock:

这个工具是在EasyMock和Mockito上扩展出来的,目的是为了解决EasyMock和Mockito不能解决的问题,比如对static, final, private方法均不能mock。其实测试架构设计良好的代码,一般并不需要这些功能,但如果是在已有项目上增加单元测试,老代码有问题且不能改时,就不得不使用这些功能了。

Spock:

spock框架是基于java和groovy脚本结合使用的  框架的设计思路参考了JUnit,jMock,RSpec,Groovy,Scala,Vulcans……

def "isUserEnabled should return true only if user status is enabled"() {

given://初始化程序

UserInfo userInfo = new UserInfo(

status: actualUserStatus

);

userDao.getUserInfo(_) >> userInfo;

expect: //简版的when then 预期设置

userService.isUserEnabled(1l) == expectedEnabled;

where:  //条件

actualUserStatus | expectedEnabled

UserInfo.ENABLED | true

UserInfo.INIT | false

UserInfo.CLOSED | false

clean //清除使用资源

}

spock也支持spy,stub之类的mock对象,但是并不推荐使用

Think twice before using this feature. It might be better to change the design of the code under specification

moco

一个简单的设置stub框架,主要用于测试和集成

Moco 可以用于移动开发,模拟尚未开发的服务;Moco 还可以用于前端开发,模拟一个完整的 Web 服务器,等等。

编译原文件:

cd

./gradlew build

启动moco服务:

java -jar moco-runner--standalone.jar start -p 12306 -c foo.json

打开浏览器 根据ip port 地址参数测试

编写配置文件:

{

"request": {

"uri" : "/event"

},

"response": {

"text": "event"

},

"on": {

"complete": {

"async" : "true",

"post" : {

"url" : "http://another_site",

"content": "content"

}

}

}

}

Jmockit:

1.简介

JMockit项目基于 Java 5 SE 的 java.lang.instrument 机制,内部使用 ASM 库来修改Java的Bytecode,是一个能帮我们解决以上问题的轻量级框架,它允许你动态的改变已有的方法,这主要基于java 1.5的Instrumentation框架,允许你重定义private,static and final方法,甚至是no-arg constructors都能够并轻易的重定义,这样便可以使得JMockit能够适应几乎所有的设计。

使用mock的场景 :

真实对象有着不确定的行为

真实对象很难创建

真实对象的行为很难触发

真实对象响应缓慢

真实对象是用户界面

真实对象使用了回调机制

真实对象尚未存在

而对应的mock具有下面的功能:

替换远程对象,如ESB、WEB Service对象等

替换复杂的对象

方便模块化开发

2.JMockit原理

JMockit是依赖JDK提供的instrument机制及ASM来实现其功能的,基本原理是这样的:

在JDK装入类的时候,由于我们设置也-javaagent,JDK会查看这个jar包的/META-INF/MANIFEST.MF文件,找到Premain-Class并加载这个类,然后调用这个类的premain方法将Instrument实现设置进去,然后JMockit就可以在类加载的时候做transformer,在做transformer的时候会通过ASM来动态改变字节码。

Jmockit也可以分类为非局部模拟与局部模拟,区分在于Expectations块是否有参数,有参数的是局部模拟,反之是非局部模拟。而Expectations块一般由Expectations类和NonStrictExpectations类定义。用Expectations类定义的,则mock对象在运行时只能按照 Expectations块中定义的顺序依次调用方法,不能多调用也不能少调用,所以可以省略掉Verifications块;而用NonStrictExpectations类定义的,则没有这些限制,所以如果需要验证,则要添加Verifications块。

JMockit有两种Mock方式:基于行为的Mock方式和基于状态的Mock方式:

(基于行为的)根据输入输出,功能测试,类似黑盒测试。需要把依赖的代码mock掉,实际相当于改变了被依赖的代码的逻辑。

(基于状态的)根据用例测试路径,测试代码内部逻辑,接近白盒测试。目的是测试单元测试及其依赖代码的调用过程,验证代码逻辑是否满足测试路径。

基于行为:

new Expectations(类){

{

期望方法;

结果;

}

}

方法调用

基于状态:

new Mockup(类) {

模拟方法{

控制该方法返回结果

}

}

pom依赖:放在junit前面 防止冲突

com.googlecode.jmockit

jmockit

${jmockit.version}

test

package com.maoyan.xuanwu.template.provider;

import com.maoyan.xuanwu.template.dao.IGoodsDAO;

import com.maoyan.xuanwu.template.domain.StorePO;

import com.maoyan.xuanwu.template.service.IGoodsService;

import com.maoyan.xuanwu.template.service.IStoreService;

import mockit.*;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import org.springframework.util.Assert;

/**

* Created by changshuchao on 2017/4/10.

*/

@RunWith(SpringJUnit4ClassRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AppServiceApplication.class)

@ContextConfiguration(locations = "classpath:applicationContext.xml")

public class GoodsServiceTest {

@Autowired

private IGoodsService goodsService;

@Autowired

private IStoreService storeService;

// @Mocked(methods = { "方法名" }, inverse = false)

// inverse=false 只有声明的method被mock 如果true 代表相反

private StorePO s = new StorePO();

//mock 构造函数

@Test

public void mockConstrutorTest() {

new Expectations() {

{

new MockUp() {

private int id;

@Mock //$init表示构造方法

public void $init(int id) {

this.id = id + 1000;

}

@Mock

public int getRealId() {

return this.id;

}

};

}

};

StorePO ss = new StorePO(1);

Assert.isTrue(ss.getRealId() == 1001);

}

//mock 属性 静态属性

@Test

public void mockFieldTest() {

new Expectations(s) {

{

Deencapsulation.setField(s, "name", "mock field");

Deencapsulation.setField(s, "bossName", "mock maoyan");

}

};

Assert.isTrue(s.getName().equals("mock field"));

Assert.isTrue(s.getBossName().equals("mock maoyan"));

}

//mock 普通方法

@Test

public void mockMethodTest() {

new Expectations(s) {

{

s.getStoreDevelop("mock");

result = "mock method";

}

};

Assert.isTrue(s.getStoreDevelop("mock").equals("mock method"));

}

//mock 私有方法

@Test

public void mockPrivateStaticMethodTest() {

new Expectations(s) {

{

Deencapsulation.invoke(s, "storeDevelop", "mock");

result = "mock private private static method";

}

};

Assert.isTrue(s.getStoreDevelop("mock").equals("mock private private static method"));

}

//mock final方法 final类直接@mocked就可以

@Test

public void mockFinalMethodTest() {

new Expectations(s) {

{

s.getStroreInfo(withAny(anyInt));

result = "mock info" + anyInt;

times = 2;//总共可以调用的次数

}

};

Assert.isTrue(s.getStroreInfo(1001).equals("mock info0"));//第一次调用走mock方法

Assert.isTrue(s.getStroreInfo(1000001).equals("mock info0"));

Assert.isTrue(s.getStroreInfo(1000).equals("1000one two threestore"));//走真实方法逻辑

}

//mock dao层接口注入到service中被调用

//mockUp 可以mock抽象类 实现里面的方法

@Test

public void mockDaoTest() {

new Expectations() {

{

MockUp mockUp = new MockUp() {

@Mock

public Double getPriceByGoodsId(int id) {

return 1.0;

}

};

IGoodsDAO goodsDAO = mockUp.getMockInstance();

this.setField(goodsService, "goodsDAO", goodsDAO);

}

};

/* MockUp mockUp = new MockUp() {

@Mock

public Double getPriceByGoodsId(int id) {

return 1.0;

}

};

IGoodsDAO goodsDAO = mockUp.getMockInstance();

new Expectations() {

{  //mock属性 将mock的dao层注入到service中

this.setField(goodsService, "goodsDAO", goodsDAO);

}

};*/

/* GoodsServiceImpl s=new GoodsServiceImpl();

s.setGoodsDAO(goodsDAO);

System.out.println(s.getTotalPrice(1));*/

//mockUp.tearDown();

Assert.isTrue(goodsService.getTotalPrice(1) == 1.0);

}

@Test

public void mockServiceTest() {

//局部参数进行mock

new Expectations(goodsService) {

{

goodsService.getTotalPrice(1);

result = 1000;

goodsService.saveOrUpdateGoods(1);

result = true;

}

};

Assert.isTrue(storeService.getPriceByid(1) == 1000.0);

}

}

你可能感兴趣的:(mock框架分享)