本篇文章给大家谈谈单元测试mock工具,以及单元测试mockup对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
今天给各位分享单元测试mock工具的知识,其中也会对单元测试mockup进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
什么是 Mock 测试?
作为动词,Mock 是模拟、模仿的意思。
作为名词,Mock 是能够模仿真实对象行为的模拟对象。
那么,在软件测试中,Mock 所模拟的对象是什么呢?
模拟的是 SUT(System Under Test:被测系统) 的依赖,而不是其本身。
比如,我要测试 A,但 A 依赖 B,要模拟的对象就是 B。
为什么要模拟 B 呢?
提高 A 的测试覆盖率: 通过 Mock 模拟 B 返回的正常和异常的结果,使用 A 的测试更充分。
避免 B 的因素对 A 产生影响: 当 B 因各种原因无法正常使用时,导致 A 无法测试。
提高 A 的测试效率: B 的真实行为可能很慢,但模拟可以很快。
Mock 的两大功能:
记录真实的调用信息
生成模拟的返回信息
使用 Mock 的问题是什么?
可能导致问题遗漏: 毕竟是模拟的,是理想可预见的情况,真实的情况可能更复杂。
可能导致维护成本变高: 接口变更 Mock 用例要跟着改,改错和漏改都可能出问题。
常见的 Mock 类型:
方法级别: Mock的对象是一个函数调用,例如:获取系统环境变量。
类级别: Mock 的对象是一个类,例如:一个 HTTP server。
接口级别: Mock 的对象是一个 API 接口。
服务级别: Mock 的对象是整个服务。
使用 Mock 做接口测试时,一般分二步:
1. 打桩: 创建 Mock 桩,指定 API 请求内容及其映射的响应内容。
2 . 调桩: 被测服务来请求 Mock 桩并接收 Mock 响应。
在这二步之间还有一个 Mock 桩的注入 , 啥是 Mock 注入?
Mock 的本质就是用模拟桩来替换真实的依赖。所谓 Mock 桩注入 就是 阻断被测服务与真实服务之间的链路,建立被测服务与 Mock 之间的链路过程。
如下图所示:
常见的方式包括但不限于以下五种:
API 请求构造
客户端 Mock:在被测服务 内部 工作,直接拦截被测服务的 API 请求方法,直接从方法内部返回预定义的 Mock 响应。
服务端 Mock:在被测服务 外部 工作,作为 HTTP 服务器接收被测服务发送的 API 请求,并返回预定义的 Mock 响应。
本地配置:
对于服务端 Mock,打桩之后会生成唯一的 Mock 桩地址,被测服务提供一个依赖服务地址配置项,在需要使用 Mock 时将依赖服务地址修改成 Mock 地址。
配置中心
对于服务端 Mock,为了避免修改依赖服务地址配置项导致被测服务重启,可以采用配置中心存储和管理依赖服务地址配置,或者使用注册中心记录服务与服务地址的映射关系。
反向代理
在微服务架构下,被测服务与依赖服务之间可能不是直连的,而是经过了一层反向代理,例如 API 网关。在这种情况下,被测服务是通过调用 API 网关来间接调用依赖服务的接口。
前向代理
服务端 Mock 除了作为 HTTP 服务器,还可以兼备 HTTP 代理的功能,这种架构又叫做 Mock 代理。
对比:
常用 Mock 工具:
单元测试级别:
easymock、jMock、Mockito、Unitils Mock、PowerMock、JMockit..
接口测试级别
Wiremock、Mockserver、Moco、Mock.js、RAP...
使用MOCK对象进行单元测试
出了什么问题? 单元测试的目标是一次只验证一个方法 小步的前进 细粒度的测试 但是假如某个方法依赖于其他一些难以操控的东东 比如说网络连接 数据库连接 或者是Servlet容器 那么我们该怎么办呢? 要是你的测试依赖于系统的其他部分 甚至是系统的多个其他部分呢?在这种情况下 倘若不小心 你最终可能会发现自己几乎初始化了系统的每个组件 而这只是为了给一个测试创造足够的运行环境让它们可以运行起来 忙乎了大半天 看上去我们好像有点违背了测试的初衷了 这样不仅仅消耗时间 还给测试过程引入了大量的耦合因素 比如说 可能有人兴致冲冲地改变了一个接口或者数据库的一张表 突然 你那卑微的单元测试的神秘的挂掉了 在这种情况发生几次之后 即使是最有耐心的开发者也会泄气 甚至最终放弃所有的测试 那样的话后果就不能想像了 再让我们看一个更加具体的情况 在实际的面向对象软件设计中 我们经常会碰到这样的情况 我们在对现实对象进行构建之后 对象之间是通过一系列的接口来实现 这在面向对象设计里是最自然不过的事情了 但是随着软件测试需求的发展 这会产生一些小问题 举个例子 用户A现在拿到一个用户B提供的接口 他根据这个接口实现了自己的需求 但是用户A编译自己的代码后 想简单模拟测试一下 怎么办呢?这点也是很现实的一个问题 我们是否可以针对这个接口来简单实现一个代理类 来测试模拟 期望代码生成自己的结果呢? 幸运的是 有一种测试模式可以帮助我们 mock对象 Mock对象也就是真实对象在调试期的替代品
现在需要Mock对象吗? 关于什么时候需要Mock对象 Tim Mackinnon给我们了一些建议 真实对象具有不可确定的行为(产生不可预测的结果 如股票的行情) 真实对象很难被创建(比如具体的web容器) 真实对象的某些行为很难触发(比如网络错误) 真实情况令程序的运行速度很慢 真实对象有用户界面 测试需要询问真实对象它是如何被调用的(比如测试可能需要验证某个回调函数是否被调用了) 真实对象实际上并不存在(当需要和其他开发小组 或者新的硬件系统打交道的时候 这是一个普遍的问题) 如何实现Mock对象? 使用mock对象进行测试的时候 我们总共需要 个步骤 分别是 使用一个接口来描述这个对象 为产品代码实现这个接口 以测试为目的 在mock对象中实现这个接口 在此我们又一次看到了针对接口编程的重要性了 因为被测试的代码只会通过接口来引用对象 所以它完全可以不知道它引用的究竟是真实的对象还是mock对象 下面看一个实际的例子 一个闹钟根据时间来进行提醒服务 如果过了下午 点钟就播放音频文件提醒大家下班了 如果我们要利用真实的对象来测试的话就只能苦苦等到下午五点 然后把耳朵放在音箱旁 我们可不想这么笨 我们应该利用mock对象来进行测试 这样我们就可以模拟控制时间了 而不用苦苦等待时钟转到下午 点钟了 下面是代码
public interface Environmental { private boolean playedWav = false; public long getTime(); public void playWavFile(String fileName); public boolean wavWasPlayed(); public void resetWav(); }
真实的实现代码
public class SystemEnvironment implements Environmental { public long getTime() { return System currentTimeMillis(); } public void playWavFile(String fileName) { playedWav = true; } public boolean wavWasPlayed() { return playedWav; } public void resetWav() { playedWav = false; } }
下面是mock对象
public class MockSystemEnvironment implements Environmental { private long currentTime; public long getTime() { return currentTime; } public void setTime(long currentTime) { this currentTime = currentTime; } public void playWavFile(String fileName) { playedWav = true; } public boolean wavWasPlayed() { return playedWav; } public void resetWav() { playedWav = false; } }
下面是一个调用getTime的具体类
import java util Calendar; public class Checker { private Environmental env; public Checker(Environmental env) { this env = env; } public void reminder() { Calendar cal = Calendar getInstance(); cal setTimeInMills(env getTime()); int hour = cal get(Calendar HOUR_OF_DAY); if(hour = ) { env playWavFile( quit_whistle wav ); } } }
使用env getTime()的被测代码并不知道测试环境和真实环境之间的区别 因为它们都实现了相同的接口 现在 你可以借助mock对象 通过把时间设置为已知值 并检查行为是否如预期那样来编写测试
import java util Calendar; import junit framework TestCase; public class TestChecker extends TestCase { public void testQuittingTime() { MockSystemEnvironment env = new MockSystemEnvironment(); Calendar cal = Calendar getInstance(); cal set(Calendar YEAR ); cal set(Calendar MONTH ); cal set(Calendar DAY_OF_MONTH ); cal set(Calendar HOUR_OF_DAY );
cal set(Calendar MINUTE ); long t = cal getTimeInMillis(); env setTime(t ); Checker checker = new Checker(env); checker reminder(); assertFalse(env wavWasPlayed()); t += ( * * ); env setTime(t ); checker reminder(); assertTrue(env wavWasPlayed()); env resetWav(); t += * * * ; env setTime(t ); checker reminder(); assertTrue(env wavWasPlayed()); } }
这就是mock对象的全部 伪装出真实世界的某些行为 使你可以集中精力测试好自己的代码
好像有一些麻烦 如果每次都像上面那样自己写具体的mock对象 问题虽然解决了 但是好像有一些麻烦 不要着急 已经有一些第三方现成的mock对象供我们使用了 使用Mock Object进行测试 主要是用来模拟那些在应用中不容易构造(如HttpServletRequest必须在Servlet容器中才能构造出来)或者比较复杂的对象(如JDBC中的ResultSet对象)从而使测试顺利进行的工具 目前 在Java阵营中主要的Mock测试工具有JMock MockCreator Mockrunner EasyMock MockMaker等 在微软的 Net阵营中主要是Nmock NetMock等 下面就以利用EasyMock模拟测试Servlet组件为例 代码如下 编译并将其当做一个Test Case运行 会发现两个测试方法均测试成功 我们可以看到easymock已经帮助我们实现了一些servlet组件的mock对象 这样我们就可以摆脱web容器和servlet容器来轻松的测试servlet了
import easymock *; import junit framework *; import javax servlet *; public class MockRequestTest extends TestCase{ private MockControl control; private HttpServletRequest mockRequest; public void testMockRequest(){ //创建一个Mock HttpServletRequest的MockControl对象 control = MockControl createControl(HttpServletRequest class); //获取一个Mock HttpServletRequest对象 mockRequest = (HttpServletRequest) control getMock(); //设置期望调用的Mock HttpServletRequest对象的方法 mockRequest getParameter( name ); //设置调用方法期望的返回值 并指定调用次数 //以下后两个参数表示最少调用一次 最多调用一次 control setReturnValue( kongxx ); //设置Mock HttpServletRequest的状态 //表示此Mock HttpServletRequest对象可以被使用 control replay(); //使用断言检查调用 assertEquals( kongxx mockRequest getParameter( name )); //验证期望的调用 control verify(); } }
编译并将其当做一个Test Case运行 会发现两个测试方法均测试成功 我们可以看到easymock已经帮助我们实现了一些servlet组件的mock对象 这样我们就可以摆脱web容器和servlet容器来轻松的测试servlet了
底层技术是什么? 让我们来回忆一下 如果用户使用C++和java的程序的生成 C++在最后的阶段还需要连接才能生成一个整体程序 这在灵活性与java源代码的机制是不能比的 java的各个类是独立的 打包的那些类也是独立的 只有在加载进去才进行连接 这在代码被加载进去的时候 我们还可以执行很多的动作 如插入一些相关的业务需求 这也是AOP的一个焦点 javassit代码库的实现类似于这 正是利用这些 所以用java实现Mock对象是很简单的
lishixinzhi/Article/program/Java/JSP/201311/19222
python单元测试--mock
使用mock,可以将某个函数所依赖的对象或者变量mock掉,从而降低测试条件的负责度。如下所示:
上述是mock对象的简单使用方法,通过实例化一个Mock对象从而模拟掉原始函数的返回值,高级一些的用法就是通过mock.patch装饰器,装饰在类或者函数上进行模拟测试,如下在test.py文件中有两个类:
测试用例设计如下:
以上测试用例说明,通过patch装饰器模拟了 test.ProductionClass1 这个类,在 test_01 中使用 mock_class 模拟 test.ProductionClass1 。首先通过 mock_class.return_value 获取类实例(如果模拟的是函数,则不需要这一步),然后通过 obj1.pro1_method.return_value 设置方法的返回值,并进行测试。测试结果说明无论是通过 mock_class 还是 test.ProductionClass1 还是 obj1 执行方法,获取到的结果都是设置的值,并且在另一个类中调用模拟类的方法,也能成功获取到设置的 return_value 。
mock单元测试 mockito实践
@Rule
public MokitoRule rule = MockitoJUnit.rule();
注意这里的修饰符public,如果没有这个修饰符的话使用mock测试会报错
@Rule
public ExpectedException thrown = ExpectedException.none();
可以选择忽视抛出的异常?(我是这么理解的不知道是否正确)
这里我还没有看juint执行的逻辑,只是看了mock环境下获取注解创建mock对象,并将mock的对向注入到@Injectmocks的目标测试对象中去的逻辑。
使用的jar包版本是junit.junit.4.11,org.powermock.powermock-api-mockito.1.6.6
创建一个interface用于目标测试类的filed的type。
创建一个目标测试类,其中声明filed的type为上面的interface的type
最后创建一个测试用例对象用于测试目标测试类
我从rule对象执行的时候开始追踪,junit的运行原理略过
其中的testClass就是测试用例,MyMockTest的实例,annotationEngine是默认的注解驱动InjectingAnnotationEngine,
这个方法的内部获取测试用例的type,获取注解驱动,并判断是否是默认的注解驱动,可以自定义注解驱动?(暂时我还办不到),之后注解驱动执行
InjectingAnnotationEngine.process 内部只有两个方法,从名字和其上的注释可以知道
processIndependentAnnotations处理独立的filed,其实就是测试用例中有@mock注解的filed,这里就是a和b。processInjectMocks处理依赖于独立mock对象的filed,就是测试用例中有@InjectMocks注解的filed,依赖于mock对象的目标测试类,这里就是DoMainObject,先看processInjectMocks
入参分别为测试用例的type,和instance,方法中只有一个循环,在循环的内部处理三件事
1delegate.process(classContext, testInstance);委派对象处理@Mock,@Captor等注解,
2spyAnnotationEngine.process(classContext, testInstance);监视注解驱动处理@Spy注解
3获取测试用例的父类,赋值给原来的变量
4如果Class的type为Object,跳出循环
这个方法就是先处理自己的独立注解,然后去处理父类的独立注解,如此往复直到父类为Object源类。
这个方法参数还是Class的type和Class 的instance,
处理过程是获取instance的所有field就是所有的属性,然后循环获取filed的上的所有注解,更具注解和field尝试创建mock对象,这里最后的创建对象时使用cblib创建代理对象,最后创建一个Setter对象将创建的cglib代理对象mock对象,set进instance的field中去,即完成了一个测试用例中的属性的注入(spring的bean注解注入方式是不是也是如此呢,所有的基于注解的实现原理是否基本类似于此呢)
这里只关注两个方法createMockFor和FieldSetter(testInstance, field).set(mock)
createMockFor方法的流程比较复杂,
这个方法的内部有两个方法,
在这个方法中对annotationd的类型与已有的注解处理器对象集合进行判断是否包含,如果包含取出对应的处理器对象,如果不包含空实现一个注解处理器实现process方法返回为null。也就是说在DefaultAnnotationEngine对象的实例中只处理特定的注解生成其mock代理对象。
这个注解处理器的集合是在4中创建了deletage时创建了DefaultAnnotationEngine对象,然后在其构造方法中调用了注册注解驱动方法
private final Map, FieldAnnotationProcessor? annotationProcessorMap = new HashMap, FieldAnnotationProcessor();
根据获取的annotationProcess对象执行process方法,如果不是从map中获取的那么返回值就是null,在本测试中就是@mock的方法返回了MockAnnotationProcessor类型的注解驱动,
Mockito.mock(field.getType(), mockSettings);这个就是最后创建mock的cglib代理对象的方法,对这个方法暂时就不继续追踪了,
现在我们已经将一个@Mock注解下的测试类中的field建立好了,让我们回到5的process方法中,能看见这个步骤就是重复的执行这段逻辑:
获取field的所有注解,调用createMockFor方法,然后在此方法中和DefaultAnnotationEngine预置的FieldAnnotationProcessor 实现类型集合做匹配,满足的获取指定的注解处理器创建对应的mock对象。不满足的创建一个匿名子类,其中实现的方法指定返回null。以此将@Mock等注解和其他注解区分开来,只创建@Mock和@Captor等独立的注解。如此步骤processIndependentAnnotations.process()就完成了。
spyAnnotationEngine.process的执行类似,但是这个注解处理类是使用反射去根据类型创建一个真实的实例对象返回而不是创建一个mock的cglib对象。
现在我们完成了processIndependentAnnotations,来看看现在的测试用例instance
可以看到现在a,b使用@Mock注解的field已经存在cglib的代理对象了,使用@InjectMocks的doMainObject暂时还是null,现在来看processInjectMocks
方法内部的核心方法是injectMocks,内部的逻辑从子类到父类最后到Object处理每个继承层级的@InjectMocks注解
方法内处理
1获取测试用例的Class类型
2创建一个Field对象的set集合
3循环处理
4将class中所有InjectMocks注解的field放到mockDependentFields集合中
5将创建的mock对象添加到mocks集合中
6Class类型是Object跳出循环
7创建@InjectMock注解修饰的field
这是根据依赖创建目标测试类的mock对象。
最后方法完成的时候
可以看到目标测试类创建完成,依赖a,b也已经注入。
没有去追踪Junit和cglib只是将中间mock的注解的过程进行了追踪:
基本就是先创建mock对象,然后将根据依赖创建@InjectMocks的目标测试类对象
其中注解的区分
1@InjectMocks和其他类型不同,
2@Spy和@Mock,@Captor等注解不同
在使用的过程中Rule一定要是pulic修饰的
在使用mockito的时候还出现了mock的对象在测试的时候报空指针的问题,我追踪后发现是同类型的interface在注入@InjectMocks修饰的主目标对象的时候是有排序的,会根据测试类中的filedName进行排序依次向下注入,解决办法就是把@InjectMocks的所有field都进行mock。
mockito单元测试
1、什么是Mock?
mock是在测试过程中,对于一些不容易构造/获取的对象,创建一个mock对象来模拟对象的行为。比如说你需要调用B服务,可是B服务还没有开发完成,那么你就可以将调用B服务的那部分给Mock掉,并编写你想要的返回结,SpringBoot默认的Mock框架是Mockito,和junit一样,只需要依赖spring-boot-starter-test就可以了。
2、测试过程中常用注解
@RunWith 指定运行环境,例:
@RunWith(SpringRunner.class) Junit运行Spring的测试环境
@RunWith(MockitoJUnitRunner.class) Junit运行Mockito的运行环境,不会加载springboot上下文
@SpringBootTest 加载springboot上下文配置
@WebMvcTest(xxxController.class) 用于测试单个Controller,提供了自配置的MocMvc,可以不启动整个应用就可以以http请求的方 式测试Controller,不过这个注解不会去扫描@Service,@Mapper等注解,当调用这些服务时最好提前打桩,不然会报Error create bean这类异常
@Mock 用于模拟一个服务,例如测试的时候需用调用B服务,但是B服务没写 好,这是可以先Mock一个B服务
@InjectMocks 标注服务的实现类,创建一个实例,其余用@Mock注解创建的mock将被注入到用该实例中
@Before 每个测试方法执行前,执行一次
@After 每个测试方法执行完,执行一次
@Test 标注测试方法
3、测试Service的简单样例,( 注:目前项目中单元测试主要测试的是逻辑,第三方api及dao层都会屏蔽掉,所以不用加载整个上下文,建议用这种方式测试)
@RunWith(MockitoJUnitRunner.class)
public classTest{
@Mock
private DataApi dataApi;
@InjectMocks
private DataServiceImpl dataService;
/*
* 准备数据
*/
@Before
public void baseInfo(){
Entity entity=new Entity();
entity.setEndTime(1652544000);
//打桩,当DataService去调用dataApi的selectByPrimaryKey时,会返回模拟的entity数据,anyLong()指任意Long型参数
doReturn(entity).when(dataApi).selectByPrimaryKey(anyLong());
}
@Test
public void testGetData(){
Stirng isOk=dataService.getData(123);
Assert.state("Ok".equals(isOk))//断言,返回预期结果,测试通过
// mock本类中的其他方法
DataServiceImpl dataService1 = Mockito.spy(dataService);
doReturn(false).when(dataService1).getData2(anyList(), any());
// 若需要多次调用一个方法,返回不同的结果,可以按顺序进行return;
when(dataService1.getData2(anyList(), any())).thenReturn("OK").thenReturn(null);
}
// 对测试类中私有变量赋值
// private int maxActive;
@Test
public void dataSourceTest(){
try {
Field apiField = FourthDaoConfiguration.class.getDeclaredField("maxActive");
FieldSetter.setField(fourthDaoConfiguration, apiField, 3);
DruidDataSource result =fourthDaoConfiguration.dataSource();
Assert.assertNotNull(result);
}catch (Exception e){
}
}
// 对测试方法中返回的公用类做mock
@Test
public void updateSystemBalanceTest(){
try {
Long userId =1L;
Integer amount =10;
String actionCause ="aa";
String swiftNumber ="aa";
RLock fairLock =mock(RedissonLock.class);
doReturn(true).when(fairLock).tryLock(50, 60, TimeUnit.SECONDS);
doNothing().when(fairLock).unlock();
doReturn(fairLock).when(redissonClient).getLock(anyString());
int result =partnerAccountActionManager.updateSystemBalance(userId, amount, actionCause, swiftNumber);
Assert.assertTrue(result 0);
}catch (Exception e){
e.printStackTrace();
}
}
}
4、测试Controller简单样例,模仿http发请求,载入springboot的上下文
@RunWith(SpringRunner.class)
@SpringBootTest
public class MockMvcTest{
// 注入web环境的ApplicationContext容器
@Autowired
private WebApplicationContext context;
/**
* 模拟mvc测试对象
*/
private MockMvc mockMvc;
@Before
public void before() throws Exception{
//获取mockmvc对象实例
mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
/*
mockMvc.perform 用于发送请求
MockMvcRequestBuilders用于指定请求方式,参数,媒体类型
*/
@Test
public void getInfo() throws Exception{
mockMvc.perform(
MockMvcRequestBuilders.get("/xxx/getInfo")//请求方式,指定URL
.param("xxxx","1489")//请求参数
.param("xxxx","232")
.param("xxxx","0")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)//媒体类型
)
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value(0))//校验响应结果
.andReturn();//返回结果
}
}
关于单元测试mock工具和单元测试mockup的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
单元测试mock工具的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于单元测试mockup、单元测试mock工具的信息别忘了在本站进行查找喔。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~