测试私有方法_史上最轻量!阿里开源了新型单元测试Mock工具
点击上方蓝色字体,选择“设为星标”
回复”666“获取面试宝典
TestableMock
是基于源码和字节码增强的Java单元测试辅助工具,包含以下功能:
- 访问被测类私有成员:使单元测试能直接调用和访问被测类的私有成员,解决私有成员初始化和私有方法测试的问题
- 快速Mock任意调用:使被测类的任意方法调用快速替换为Mock方法,实现"指哪换哪",解决传统Mock工具使用繁琐的问题
- 辅助测试void方法:利用Mock校验器对方法的内部逻辑进行检查,解决无返回值方法难以实施单元测试的问题
访问私有成员字段和方法
如今关于私有方法是否应该做单元测试的争论正逐渐消停,开发者的普遍实践已经给出事实答案。通过公有方法间接测私有方法在很多情况下难以进行,开发者们更愿意通过修改方法可见性的办法来让原本私有的方法在测试用例中变得可测。
此外,在单元测试中时常会需要对被测对象进行特定的成员字段初始化,但有时由于被测类的构造方法限制,使得无法便捷的对这些字段进行赋值。那么,能否在不破坏被测类型封装的情况下,允许单元测试用例内的代码直接访问被测类的私有方法和成员字段呢?TestableMock提供了两种简单的解决方案。
方法一:使用@EnablePrivateAccess
注解
只需为测试类添加@EnablePrivateAccess
注解,即可在测试用例中获得以下增强能力:
- 调用被测类的私有方法(包括静态方法)
- 读取被测类的私有字段(包括静态字段)
- 修改被测类的私有字段(包括静态字段)
- 修改被测类的常量字段(使用final修饰的字段,包括静态字段)
访问和修改私有、常量成员时,IDE可能会提示语法有误,但编译器将能够正常运行测试。(使用编译期代码增强,目前仅实现了Java语言的适配)
效果见java-demo
示例项目DemoPrivateAccessTest
测试类中的用例。
方法二:使用PrivateAccessor
工具类
若不希望看到IDE的语法错误提醒,或是在非Java语言的JVM工程(譬如Kotlin语言)里,也可以借助PrivateAccessor
工具类来直接访问私有成员。
这个类提供了6个静态方法:
PrivateAccessor.get(被测对象, "私有字段名")
➜ 读取被测类的私有字段PrivateAccessor.set(被测对象, "私有字段名", 新的值)
➜ 修改被测类的私有字段(或常量字段)PrivateAccessor.invoke(被测对象, "私有方法名", 调用参数..)
➜ 调用被测类的私有方法PrivateAccessor.getStatic(被测类型, "私有静态字段名")
➜ 读取被测类的静态 私有字段PrivateAccessor.setStatic(被测类型, "私有静态字段名", 新的值)
➜ 修改被测类的静态 私有字段(或静态 常量字段)PrivateAccessor.invokeStatic(被测类型, "私有静态方法名", 调用参数..)
➜ 调用被测类的静态 私有方法
快速Mock被测类的任意方法调用
相比以往Mock工具以类为粒度的Mock方式,TestableMock
允许用户直接定义需要Mock的单个方法,并遵循约定优于配置的原则,按照规则自动在测试运行时替换被测方法中的指定方法调用。
归纳起来就两条:
- Mock非构造方法,拷贝原方法定义到测试类,增加一个与调用者类型相同的参数,加
@MockMethod
注解- Mock构造方法,拷贝原方法定义到测试类,返回值换成构造的类型,方法名随意,加
@MockContructor
注解
具体的Mock方法定义约定如下:
1. 覆写任意类的方法调用
在测试类里定义一个有@MockMethod
注解的普通方法,使它与需覆写的方法名称、参数、返回值类型完全一致,然后在其参数列表首位再增加一个类型为该方法原本所属对象类型的参数。
此时被测类中所有对该需覆写方法的调用,将在单元测试运行时,将自动被替换为对上述自定义Mock方法的调用。
注意 :当遇到待覆写方法有重名时,可以将需覆写的方法名写到@MockMethod
注解的targetMethod
参数里,这样Mock方法自身就可以随意命名了。
例如,被测类中有一处"anything".substring(1, 2)
调用,我们希望在运行测试的时候将它换成一个固定字符串,则只需在测试类定义如下方法:
// 原方法签名为`String substring(int, int)`// 调用此方法的对象`"anything"`类型为`String`// 则Mock方法签名在其参数列表首位增加一个类型为`String`的参数(名字随意)// 此参数可用于获得当时的实际调用者的值和上下文@MockMethodprivate String substring(String self, int i, int j) { return "sub_string";}
下面这个例子展示了targetMethod
参数的用法,其效果与上述示例相同:
// 使用`targetMethod`指定需Mock的方法名// 此方法本身现在可以随意命名,但方法参数依然需要遵循相同的匹配规则@MockMethod(targetMethod = "substring")private String use_any_mock_method_name(String self, int i, int j) { return "sub_string";}
完整代码示例见java-demo
和kotlin-demo
示例项目中的should_able_to_mock_common_method()
测试用例。(由于Kotlin对String类型进行了魔改,故Kotlin示例中将被测方法在BlackBox
类里加了一层封装)
2. 覆写被测类自身的成员方法
有时候,在对某些方法进行测试时,希望将被测类自身的另外一些成员方法Mock掉。
操作方法与前一种情况相同,Mock方法的第一个参数类型需与被测类相同,即可实现对被测类自身(不论是公有或私有)成员方法的覆写。
例如,被测类中有一个签名为String innerFunc(String)
的私有方法,我们希望在测试的时候将它替换掉,则只需在测试类定义如下方法:
// 被测类型是`DemoMock`// 因此在定义Mock方法时,在目标方法参数首位加一个类型为`DemoMock`的参数(名字随意)@MockMethodprivate String innerFunc(DemoMock self, String text) { return "mock_" + text;}
3. 覆写任意类的静态方法
对于静态方法的Mock与普通方法相同。但需要注意的是,静态方法的Mock方法被调用时,传入的第一个参数实际值始终是null
。
例如,在被测类中调用了BlackBox
类型中的静态方法secretBox()
,改方法签名为BlackBox secretBox()
,则Mock方法如下:
// 目标静态方法定义在`BlackBox`类型中// 在定义Mock方法时,在目标方法参数首位加一个类型为`BlackBox`的参数(名字随意)// 此参数仅用于标识目标类型,实际传入值将始终为`null`@MockMethodprivate BlackBox secretBox(BlackBox ignore) { return new BlackBox("not_secret_box");}
完整代码示例见java-demo
和kotlin-demo
示例项目中的should_able_to_mock_static_method()
测试用例。
测试无返回值的方法
如何对void类型的方法进行测试一直是许多单元测试框架在悄悄回避的话题,由于以往的单元测试手段主要是对被测单元的返回结果进行校验,当遇到方法没有返回值时就会变得无从下手。
从功能的角度来说,虽然void方法不返回任何值,但它的执行一定会对外界产生某些潜在影响,我们将其称为方法的"副作用",比如:
- 初始化某些外部变量(私有成员变量或者全局静态变量)
- 在方法体内对外部对象实例进行赋值
- 输出了日志
- 调用了其他外部方法
- … …
不返回任何值也不产生任何"副作用"的方法没有存在的意义。
这些"副作用"的本质归纳来说可分为两类:修改外部变量 和调用外部方法 。
通过TestableMock的私有字段访问和Mock校验器可以很方便的实现对"副作用"的结果检查。
1. 修改外部变量的void方法
例如,下面这个方法会根据输入修改私有成员变量hashCache
:
class Demo { private Map hashCache = mapOf();public void updateCache(String domain, String key) { String cacheKey = domain + "::" + key; Integer num = hashCache.get(cacheKey); hashCache.put(cacheKey, count == null ? initHash(key) : nextHash(num, key)); } ... // 其他方法省略}
若要测试此方法,可以利用TestableMock直接读取私有成员变量的值,对结果进行校验:
@EnablePrivateAccess // 启用TestableMock的私有成员访问功能class DemoTest { private Demo demo = new Demo();
@Test public void testSaveToCache() { Integer firstVal = demo.initHash("hello"); // 访问私有方法 Integer nextVal = demo.nextHash(firstVal, "hello"); // 访问私有方法 demo.saveToCache("demo", "hello"); assertEquals(firstVal, demo.hashCache.get("demo::hello")); // 读取私有变量 demo.saveToCache("demo", "hello"); assertEquals(nextVal, demo.hashCache.get("demo::hello")); // 读取私有变量 }}
2. 调用外部方法的void方法
例如,下面这个方法会根据输入打印信息到控制台:
class Demo { public void recordAction(Action action) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss "); String timeStamp = df.format(new Date()); System.out.println(timeStamp + "[" + action.getType() + "] " + action.getTarget()); }}
若要测试此方法,可以利用TestableMock快速Mock掉System.out.println
方法。在Mock方法体里可以继续执行原调用(相当于并不影响本来方法功能,仅用于做调用记录),也可以直接留空(相当于去除了原方法的副作用)。
在执行完被测的void类型方法以后,用InvokeVerifier.verify()
校验传入的打印内容是否符合预期:
class DemoTest { private Demo demo = new Demo();
// 拦截`System.out.println`调用 @MockMethod public void println(PrintStream ps, String msg) { // 执行原调用 ps.println(msg); }
@Test public void testRecordAction() { Action action = new Action("click", ":download"); demo.recordAction(); // 验证Mock方法`println`被调用,且传入参数符合预期 verify("println").with(matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\[click\\] :download")); }}
项目地址
开源地址:https://gitee.com/mirrors/TestableMock
来源 | gitee.com/mirrors/TestableMock
面试官问:MySQL的自增ID用完了,怎么办?
ArrayList插入1000w条数据之后,我怀疑了jvm...
蚂蚁二面,面试官问我零拷贝的实现原理,当场懵了…
最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡
测试私有方法_史上最轻量!阿里开源了新型单元测试Mock工具相关推荐
- jq+ajax前端上传多张图片_史上最轻量的前端框架-VanillaJS
vanillaJS是史上最轻量跨平台前端框架 ,我们能够使用它构建强大的JS应用.从过去到以后他都是最轻量的框架 介绍 vanillaJS目前在国外备受推崇,Bootstrap5舍弃了Jquery,选 ...
- K3s初探:Rancher架构师带你尝鲜史上最轻量Kubernetes发行版
发布不到两天,GitHub上Star数已近3000,这个业界大热的.史上最轻量的开源Kubernetes发行版,你试过了没? Rancher资深架构师来教你走出尝鲜第一步!使用教程在此! 前 言 昨天 ...
- 测试框架 如何测试私有方法_高效的企业测试–测试框架(5/6)
测试框架 如何测试私有方法 本系列文章的这一部分将介绍测试框架以及我在何时以及是否应用它们方面的想法和经验. 关于测试框架的想法 我对大多数测试框架不太满意的原因是,按照我的观点,它们大多增加了语法上 ...
- 史上最轻量!阿里新型单元测试Mock工具开源了
简介:为了探索更轻量易用的Mock测试手段,阿里云云效团队尝试给工具减负,在主流Mock工具的基础上让Mock的定义和置换干净利落,最终设计了一款极简风格的测试辅助工具TestableMock,无需初 ...
- 暴露的全局方法_史上最便捷搭建 ZooKeeper 服务器的方法
什么是 ZooKeeper ZooKeeper 是 Apache 的一个顶级项目,为分布式应用提供高效.高可用的分布式协调服务,提供了诸如数据发布/订阅.负载均衡.命名服务.分布式协调/通知和分布式锁 ...
- 史上最轻量K8s发行版,赋能边缘计算
2019年2月26日,业界领先的容器管理软件提供商Rancher Labs(以下简称Rancher)宣布推出轻量级Kubernetes发行版K3s,这款产品专为在资源有限的环境中运行Kubernete ...
- python方法测试怀孕_在Python中测试私有方法(例外)
在阅读了关于在Python中测试私有方法的内容之后,特别是在How do I unit test the methods in a method object?处引用了接受的答案,看来最好只测试公共接 ...
- powermockito测试私有方法_03 增强测试: 静态、私有方法处理
mockito 已经很强大,能帮我们完成大部分 mock 工作,但是对于一些特殊方法来说,还是无能为力. 例如,当我们使用系统获取当前时间戳的时候,可能会调用 System.currentTimeMi ...
- 戴森史上最轻吸尘器全球首发,搭配吸头后仅重1.5千克
戴森Digital Slim轻量[2]无绳吸尘器于中国市场全球首发[3].它是戴森无绳吸尘器中重量最轻[1]的一款产品,搭配吸头后仅重1.5千克[4],重量减轻30%[5].体积减小20%[5],性能 ...
最新文章
- Mybaits整合Spring自动扫描 接口,Mybaits配置文件.xml文件和Dao实体类
- C语言递归算法十进制数转换为八进制(附完整源码)
- 2021年上半年金融科技动向
- nuxt generate 报错 window is not defined
- 图像分类中数据增强的有效性
- 没关系,因为你是好人呀
- 一直困扰我的String判空这回终于有解决办法了
- 微信小程序视频自定义进度条
- linux下kegg注释软件,网页工具KOBAS进行KEGG富集分析
- 【网络安全学习实践】Windows基本DOS文件命令与简易病毒编写
- 概率论知识回顾(二):古典概型,几何概型
- php图标源码,FaviconICO图标制作在线制作生成PHP开源版源码
- echart图表-刷新界面.初始化时默认显示hover提示内容
- ANSVC无功补偿装置在南京某高等院校中的应用-安科瑞华楠
- 移动最小二乘实现点云插值(上采样 | 增采样)详细讲解❤️❤️❤️
- 一个arm64国产化工控机工程的移植总结
- 禁用Windows Defender Antivirus Service,让你文件正常运行
- 地统计插值学习心得(三)ArcGIS Pro与ArcMap软件中地统计分析的区别
- restcontrol 注解
- xstart下远程报错已拒绝X11转移申请的解决方法