mock框架分享

什么是单元测试?

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

一、mock框架功能对比:

二、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);

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,275评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,049评论 18 139
  • Instrumentation介绍 Instrumentation是个什么东西? Instrumentation测...
    打不死的小强qz阅读 7,652评论 2 39
  • Martin Fowler的一篇文章。  Key point: two differences; SUT  'M...
    Luna_Lu阅读 1,542评论 0 4
  • 9月11日 我:我们要准备晨圈啦,大家要把玩具收起来啦,陈皮你可以帮忙一起收吗? 陈皮很快就过来了,他蹲了下来,把...
    美育Alice阅读 196评论 0 0