单元测试框架TestableMock快速入门(三):校验Mock调用
目录
一、概述
二、基本校验器
三、基本匹配器
四、空值匹配器
五、字符串匹配器
六、万能匹配器
七、使用示例
一、概述
在测试中,除了需要将某些含有外部依赖的方法替换为Mock,经常还会需要验证该方法被调用时的参数是否符合预期。
在TestableMock
中提供了校验器(verifier)和匹配器(matcher)来实现这一功能。譬如:
@Test
public test_case() {int res = insToTest.methodToTest();verify("mockMethod").with(123, "abc");
}
这个用例会检查在执行被测方法methodToTest()
时,名称是mockMethod
的Mock方法是否有被调用过,且调用时收到的参数值是否为123
和"abc"
(假设被Mock的mockMethod
方法有两个参数)。
除了这种简单校验以外,TestableMock
当前已经支持了多种校验器,以及能够模糊匹配参数特征的匹配器。
二、基本校验器
with(Object... args)
→ 验证方法是否被指定参数调用过;withInOrder(Object... args)
→ 如果指定方法被调用了多次,依据实际调用顺序依次匹配;withTimes(int expectedCount)
→ 验证方法是否被调用过指定次数,忽略对调用参数的检查;without(Object... args)
→ 验证方法从未被使用指定参数调用过;times(int count)
→ 连在with()
或withInOrder()
方法之后使用,验证该方法被同样条件的参数调用过了指定次数;
三、基本匹配器
any()
→ 匹配任何值,包括Nullany(Class<?> clazz)
→ 匹配任何指定类型或子类型的值anyTypeOf(Class<?>... classes)
→ 匹配在列表中的任意一种类型的值anyString()
→ 匹配任何字符串anyNumber()
→ 匹配任何数值(整数或浮点数)anyBoolean()
→ 匹配任何布尔值anyByte()
→ 匹配任何单字节类型的值anyChar()
→ 匹配任何单字符类型的值anyInt()
→ 匹配任何整数类型的值anyLong()
→ 匹配任何长整数类型的值anyFloat()
→ 匹配任何浮点数类型的值anyDouble()
→ 匹配任何双精度浮点数类型的值anyShort()
→ 匹配任何短整数类型的值anyArray()
→ 匹配任何数组anyArrayOf(Class<?> clazz)
→ 匹配任何指定类型的数组anyList()
→ 匹配任何列表anyListOf(Class<?> clazz)
→ 匹配任何指定类型的列表anySet()
→ 匹配任何集合anySetOf(Class<?> clazz)
→ 匹配任何指定类型的集合anyMap()
→ 匹配任何映射anyMapOf(Class<?> keyClass, Class<?> valueClass)
→ 匹配任何指定类型的映射anyCollection()
→ 匹配任何容器anyCollectionOf(Class<?> clazz)
→ 匹配任何指定类型的容器anyIterable()
→ 匹配任何迭代器anyIterableOf(Class<?> clazz)
→ 匹配任何指定类型的迭代器eq(Object obj)
→ 匹配与指定值相等的对象refEq(Object obj)
→ 匹配指定对象(非值相等,而是就是同一个对象)
四、空值匹配器
isNull()
→ 匹配NullnotNull()
→ 匹配除Null以外的任何值nullable(Class<?> clazz)
→ 匹配空或指定类型的任何值
五、字符串匹配器
contains(String substring)
→ 匹配包含特定子串的字符串matches(String regex)
→ 匹配符合指定正则表达式的字符串endsWith(String suffix)
→ 匹配以指定子串结尾的字符串startsWith(String prefix)
→ 匹配以指定子串开头的字符串
六、万能匹配器
any(MatchFunction matcher)
→ 匹配符合指定表达式的值
七、使用示例
【a】编写被测试类
package com.wsh.testable.mock.testablemockdemo;import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;/*** 演示Mock方法调用校验器*/
public class DemoMatcher {/*** Method to be mocked*/private void methodToBeMocked() {// pretend to have some code here}/*** Method to be mocked*/private void methodToBeMocked(Object a1, Object a2) {// pretend to have some code here}/*** Method to be mocked*/private void methodToBeMocked(Object[] a) {// pretend to have some code here}public void callMethodWithoutArgument() {methodToBeMocked();}public void callMethodWithNumberArguments() {// named variable and lambda variable will be recorded as different type// should have them both in test caseList<Float> floatList = new ArrayList<>();floatList.add(1.0F);floatList.add(2.0F);Long[] longArray = new Long[]{1L, 2L};methodToBeMocked(1, 2);methodToBeMocked(1L, 2.0);methodToBeMocked(new ArrayList<Integer>(){{ add(1); }}, new HashSet<Float>(){{ add(1.0F); }});methodToBeMocked(1.0, new HashMap<Integer, Float>(2){{ put(1, 1.0F); }});methodToBeMocked(floatList, floatList);methodToBeMocked(longArray);methodToBeMocked(new Double[]{1.0, 2.0});}public void callMethodWithStringArgument() {methodToBeMocked("hello", "world");methodToBeMocked("testable", "mock");methodToBeMocked(new String[]{"demo"});}public void callMethodWithObjectArgument() {methodToBeMocked(new BlackBox("hello"), new BlackBox("world"));methodToBeMocked(new BlackBox("demo"), null);methodToBeMocked(null, new BlackBox("demo"));}}
【b】编写测试类
package com.wsh.testable.mock.testablemockdemo;import com.alibaba.testable.core.annotation.MockMethod;
import com.alibaba.testable.core.error.VerifyFailedError;
import org.junit.jupiter.api.Test;import static com.alibaba.testable.core.matcher.InvokeMatcher.any;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyArray;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyArrayOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyInt;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyList;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyListOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyLong;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyMapOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyNumber;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anySetOf;
import static com.alibaba.testable.core.matcher.InvokeMatcher.anyString;
import static com.alibaba.testable.core.matcher.InvokeMatcher.contains;
import static com.alibaba.testable.core.matcher.InvokeMatcher.endsWith;
import static com.alibaba.testable.core.matcher.InvokeMatcher.isNull;
import static com.alibaba.testable.core.matcher.InvokeMatcher.matches;
import static com.alibaba.testable.core.matcher.InvokeMatcher.notNull;
import static com.alibaba.testable.core.matcher.InvokeMatcher.nullable;
import static com.alibaba.testable.core.matcher.InvokeMatcher.startsWith;
import static com.alibaba.testable.core.matcher.InvokeVerifier.verify;
import static org.junit.jupiter.api.Assertions.fail;/*** 演示Mock方法调用校验器*/
class DemoMatcherTest {/*** 被测试类*/private DemoMatcher demoMatcher = new DemoMatcher();/*** Mock容器*/public static class Mock {/*** 未指明targetClass,默认重写第一个参数类型里面,即DemoMatcher的methodToBeMocked()方法*/@MockMethod(targetMethod = "methodToBeMocked")private void methodWithoutArgument(DemoMatcher self) {}/*** Mock DemoMatcher被测类的methodToBeMocked(Object a1, Object a2)方法*/@MockMethod(targetMethod = "methodToBeMocked")private void methodWithArguments(DemoMatcher self, Object a1, Object a2) {}/*** Mock DemoMatcher被测类的methodToBeMocked(Object[] a)方法*/@MockMethod(targetMethod = "methodToBeMocked")private void methodWithArrayArgument(DemoMatcher self, Object[] a) {}}/*** 1. withTimes(int expectedCount) → 验证方法是否被调用过指定次数,忽略对调用参数的检查*/@Testvoid should_match_no_argument() {//callMethodWithoutArgument()方法内部调用了methodToBeMocked()方法demoMatcher.callMethodWithoutArgument();//检查在执行被测方法callMethodWithoutArgument()时,名称是methodWithoutArgument的Mock方法是否有被调用过一次,并忽略对调用参数的检查verify("methodWithoutArgument").withTimes(1);demoMatcher.callMethodWithoutArgument();//检查在执行被测方法callMethodWithoutArgument()时,名称是methodWithoutArgument的Mock方法是否有被调用过两次,并忽略对调用参数的检查verify("methodWithoutArgument").withTimes(2);}/*** 2.with(Object... args) → 验证方法是否被指定参数调用过* withInOrder(Object... args) → 如果指定方法被调用了多次,依据实际调用顺序依次匹配* without(Object... args) → 验证方法从未被使用指定参数调用过*/@Testvoid should_match_number_arguments() {//callMethodWithNumberArguments()方法内部调用了methodToBeMocked()很多重载的方法demoMatcher.callMethodWithNumberArguments();//验证Mock方法methodWithArguments()从未被使用指定参数调用过verify("methodWithArguments").without(anyString(), 2);//withInOrder(Object... args) → 如果指定方法被调用了多次,依据实际调用顺序依次匹配verify("methodWithArguments").withInOrder(anyInt(), 2);verify("methodWithArguments").withInOrder(anyLong(), anyNumber());//验证Mock方法methodWithArguments()是否被指定参数调用过verify("methodWithArguments").with(1.0, anyMapOf(Integer.class, Float.class));verify("methodWithArguments").with(anyList(), anySetOf(Float.class));verify("methodWithArguments").with(anyList(), anyListOf(Float.class));//验证Mock方法methodWithArrayArgument()是否被指定参数调用过verify("methodWithArrayArgument").with(anyArrayOf(Long.class));verify("methodWithArrayArgument").with(anyArray());}/*** 3.* contains(String substring) → 匹配包含特定子串的字符串* matches(String regex) → 匹配符合指定正则表达式的字符串* endsWith(String suffix) → 匹配以指定子串结尾的字符串* startsWith(String prefix) → 匹配以指定子串开头的字符串*/@Testvoid should_match_string_arguments() {//注意callMethodWithStringArgument()在类DemoMatcher中的实现demoMatcher.callMethodWithStringArgument();//验证Mock方法methodWithArguments()是否被指定参数调用过,参数分别满足以"he"开头、以"ld"结尾verify("methodWithArguments").with(startsWith("he"), endsWith("ld"));//参数类型:包含"stab"、匹配"m.[cd]k"正则表达式verify("methodWithArguments").with(contains("stab"), matches("m.[cd]k"));//参数类型:String类型的数组verify("methodWithArrayArgument").with(anyArrayOf(String.class));}/*** 4.* withInOrder(Object... args) → 如果指定方法被调用了多次,依据实际调用顺序依次匹配*/@Testvoid should_match_object_arguments() {demoMatcher.callMethodWithObjectArgument();verify("methodWithArguments").withInOrder(any(BlackBox.class), any(BlackBox.class));verify("methodWithArguments").withInOrder(nullable(BlackBox.class), nullable(BlackBox.class));verify("methodWithArguments").withInOrder(isNull(), notNull());}/*** 5.* times(int count) → 连在with()或withInOrder()方法之后使用,验证该方法被同样条件的参数调用过了指定次数*/@Testvoid should_match_with_times() {demoMatcher.callMethodWithNumberArguments();verify("methodWithArguments").with(anyNumber(), any()).times(3);demoMatcher.callMethodWithNumberArguments();boolean gotError = false;try {verify("methodWithArguments").with(anyNumber(), any()).times(4);} catch (VerifyFailedError e) {gotError = true;}if (!gotError) {fail();}}}
本案例来自官网, 笔者只是做一个学习总结。更详细使用介绍参考官网:TestableMock
单元测试框架TestableMock快速入门(三):校验Mock调用相关推荐
- 分布式应用框架Akka快速入门
转自:分布式应用框架Akka快速入门_jmppok的专栏-CSDN博客_akka 本文结合网上一些资料,对他们进行整理,摘选和翻译而成,对Akka进行简要的说明.引用资料在最后列出. 1.什么是Akk ...
- sql语言和php,SQL语言快速入门(三)_php
我们日常使用SQL语言的工作过程中,使用最多的还是从已经建立好的数据库中查询信息.下面,我们就来详细介绍一下如何使用SQL语言实现各种数据库查询操作. SELECT-FROM 为方便讲解,我们在数据库 ...
- Struts2入门第一讲——Struts2框架的快速入门
Struts2框架的概述 Struts2是一种基于MVC模式的轻量级Web框架,它自问世以来,就受到了广大Web开发者的关注,并广泛应用于各种企业系统的开发中.目前掌握Struts2框架几乎成为Web ...
- Spring入门第一讲——Spring框架的快速入门
Spring的概述 什么是Spring? 我们可以从度娘上看到这样有关Spring的介绍: 说得更加详细一点,Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架, ...
- go语言MVC框架beego快速入门
beego快速入门 beego 是一个快速开发 Go 应用的 HTTP 框架,他可以用来快速开发 API.Web 及后端服务等各种应用,是一个 RESTful 的框架,主要设计灵感来源于 tornad ...
- php yii2框架教程,Yii2框架使用快速入门
Yii 是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用. 因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用, 如门户网站.社区.内容管理系统(CM ...
- AS3多线程快速入门(三):NAPE物理引擎+Starling
原文:http://blog.domlib.com/articles/345 [更新]Adobe在11.4正式发布的最后一刻移除了ByteArray.shareable功能的支持,推迟到11.5版本再 ...
- AS3多线程快速入门(三):NAPE物理引擎+Starling[译]
原文链接:http://esdot.ca/site/2012/intro-to-as3-workers-part-3-nape-physics-starling [更新]Adobe在11.4正式发布的 ...
- Shiro安全框架【快速入门】就这一篇!
Shiro 简介 照例又去官网扒了扒介绍: Apache Shiro™ is a powerful and easy-to-use Java security framework that perfo ...
- Python 什么是flask框架?快速入门
一.Python flask框架 前言 1.Python 面向对象的高级编程语言,以其语法简单.免费开源.免编译扩展性高,同时也可以嵌入到C/C++程序和丰富的第三方库,Python运用到大数据分析. ...
最新文章
- python基础与大数据_Python大数据基础与实战第10章数据可视化.pptx
- 统一认证服务CAS 5安装
- Entity Framework 4 Poco开发之旅 part 2
- JLabel跟label
- php截断上传,截断在文件包含和上传中的利用
- 提高数据库查询速度的几个思路
- Linux VFS的主要的数据结构
- Google Puppeteer加入到headless Chrome的工具行列
- 第2章[2.8] Ext JS的控制器类型及使用
- 如何实现在Windows上运行Linux程序,附示例代码
- 如何测网络稳定性_讲座|复杂网络上的非线性动力学:网络结构如何决定系统稳定性?...
- hibernate+spring+struts集成,并自动生成实体类和DAO层的步奏
- Python实现代码雨效果
- Ubuntu 20.04安装微信,QQ,TIM
- buildroot external-toolchain的一次踩坑经历
- 8 款强大且免费的 MySQL 数据库建模工具
- 物联网与互联网有什么区别
- python手游自动化测试流程_基于Python+appium的ios自动化测试demo(更新中)
- 切换无线网卡失败服务器提示,无线网卡切换为AP模式时提示ICS启动失败的解决方法...
- FileZilla 下载
热门文章
- linux安装qq权限不够,[操作系统]Linuxqq安装及其所引发的问题{权限位是 777 (必须 =0755 且 =0755)}...
- 如何用计算机制作统计图,统计图制作_ai的环形统计图怎么制作
- 什么是java OOM Out Of Memory 内存溢出?如何分析及解决oom问题?
- mysql grant tables_使用SKIP-GRANT-TABLES 解决 MYSQL ROOT密码丢失
- 高德地图android显示级别指定位置,获取地图中心点/级别
- pwm 正弦波_增强型PWM抑制功能对于直列式电机控制的五大优势
- (1)GO数据库存储结构详解(从数据库构建角度解析)
- 最新关于高德地图定位失败10:定位服务启动、解决办法
- 计算机组成原理完整学习笔记(五):计算方法
- 如果一非零的整系数多项式能够分解成两个次数较低的有理系数多项式的乘积,那么它一定能分解成两个次数较低的整系数多项式的乘积