测试私有方法_史上最轻量!阿里开源了新型单元测试Mock工具

点击上方蓝色字体,选择“设为星标”

回复”666“获取面试宝典

1e1087a2f9d67e4c952251cb4506ce13.png

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`的参数(名字随意)
// 此参数可用于获得当时的实际调用者的值和上下文
@MockMethod
private 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-demokotlin-demo示例项目中的should_able_to_mock_common_method()测试用例。(由于Kotlin对String类型进行了魔改,故Kotlin示例中将被测方法在BlackBox类里加了一层封装)

2. 覆写被测类自身的成员方法

有时候,在对某些方法进行测试时,希望将被测类自身的另外一些成员方法Mock掉。

操作方法与前一种情况相同,Mock方法的第一个参数类型需与被测类相同,即可实现对被测类自身(不论是公有或私有)成员方法的覆写。

例如,被测类中有一个签名为String innerFunc(String)的私有方法,我们希望在测试的时候将它替换掉,则只需在测试类定义如下方法:

// 被测类型是`DemoMock`
// 因此在定义Mock方法时,在目标方法参数首位加一个类型为`DemoMock`的参数(名字随意)
@MockMethod
private String innerFunc(DemoMock self, String text) {
    return "mock_" + text;
}

3. 覆写任意类的静态方法

对于静态方法的Mock与普通方法相同。但需要注意的是,静态方法的Mock方法被调用时,传入的第一个参数实际值始终是null

例如,在被测类中调用了BlackBox类型中的静态方法secretBox(),改方法签名为BlackBox secretBox(),则Mock方法如下:

// 目标静态方法定义在`BlackBox`类型中
// 在定义Mock方法时,在目标方法参数首位加一个类型为`BlackBox`的参数(名字随意)
// 此参数仅用于标识目标类型,实际传入值将始终为`null`
@MockMethod
private BlackBox secretBox(BlackBox ignore) {
    return new BlackBox("not_secret_box");
}

完整代码示例见java-demokotlin-demo示例项目中的should_able_to_mock_static_method()测试用例。

测试无返回值的方法

如何对void类型的方法进行测试一直是许多单元测试框架在悄悄回避的话题,由于以往的单元测试手段主要是对被测单元的返回结果进行校验,当遇到方法没有返回值时就会变得无从下手。

从功能的角度来说,虽然void方法不返回任何值,但它的执行一定会对外界产生某些潜在影响,我们将其称为方法的"副作用",比如:

  1. 初始化某些外部变量(私有成员变量或者全局静态变量)
  2. 在方法体内对外部对象实例进行赋值
  3. 输出了日志
  4. 调用了其他外部方法
  5. … …

不返回任何值也不产生任何"副作用"的方法没有存在的意义。

这些"副作用"的本质归纳来说可分为两类:修改外部变量 和调用外部方法 。

通过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/TestableMock8ac1dea8429019f2b5c45b3ae8ef1d34.gif
  • 面试官问:MySQL的自增ID用完了,怎么办?

  • ArrayList插入1000w条数据之后,我怀疑了jvm...

  • 蚂蚁二面,面试官问我零拷贝的实现原理,当场懵了…

3132a1686263bcdf3fb9e74cf0667502.png

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/520393.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云云效如何保障双11大型项目管理

每一年双11对阿里巴巴来说都是一个练兵场,一次更大规模技术创新、产品迭代、协同研发的练兵场。在2018年双11的“练兵场上”,阿里云云效——一站式企业协同研发平台支持着阿里巴巴众多部门、百个核心产品、千个垂直项目、几千人共同协作完成双十一大促的…

揭秘天猫双11背后:20万商家600万张海报,背后只有一个鹿班

还记得去年双11,秋裤厂商带着“五彩斑斓的黑”需求找设计师的故事吗? 现在,已经有超过20万客户把这个AI设计师鹿班带回家。 今年,鹿班除了为天猫淘宝平台提供服务之外,还通过阿里云全面为各行业客户输出AI设计能力。…

华为开源数据虚拟化引擎HetuEngine;全球超算500强:中国上榜数量增加;谷歌收购云计算公司CouldSimple ……...

关注并标星星CSDN云计算 速递、最新、绝对有料。这里有企业新动、这里有业界要闻,打起十二分精神,紧跟fashion你可以的!每周两次,打卡即read更快、更全了解泛云圈精彩newsgo go go华为V30 渲染图:取消3.5mm耳机孔&…

三位数除以两位数怎么算竖式_青岛版三年级上册数学5.6三位数除以一位数(最高位商后有余数)微课知识点精讲+练习...

微课视频第一课时微课视频第二课时同步练习1、列竖式计算下面各题3753 2382 5884 62352、一片树林3年共增加了537棵树,平均每年增长多少棵树?3、新华小学一年级共报名448人,如果一年级预开设8个班,那每个班有多少人…

阿里云移动研发平台EMAS,是如何连续5年安全护航双11的?

2014年11月11日,阿里巴巴天猫双十一购物狂欢节全天交易额571亿元;2015年交易额912.17亿元; 2016年1207亿 ;2017年1682亿元;2018年2135亿。五年内交易量不断攀升,屡创新高,这让双十一成为了全球商…

从双十一看阿里云安全的“创世纪”——采访阿里云安全掌门人肖力有感

“神说要有光于是就有了光。”在双十一采访了阿里云安全掌门人肖力之后,忽然对这句话有了全新的认识。从肖力的介绍中我们不难体会到,与云计算相伴而生的阿里云安全诞生的必然性,云的先天基因赐予它“原生的神力”,从最初单纯的攻…

这段 Python 代码让程序员赚 300W,公司已确认!网友:神操作!

Python到底还能给人多少惊喜?笔者最近看到了这两天关于Python最热门的话题,关于《地产大佬潘石屹学Python的原因》,结果被这个回答惊到了:来源:知乎 https://www.zhihu.com/question/355880221笔者翻了翻那些回答&…

奉上一份云上数据安全保护指南

阿里云资深安全专家黄瑞瑞 本方案的目标是为用户提供从底层云平台数据安全到上层的云上环境保护,并标明各层次模块,让用户可以像建房子一样,一层层的搭建可信的在云上数据的安全保护。在各横向层次模块之外,云上数据安全也需要纵…

云数据库POLARDB优势解读系列文章之②——高性价比

现在做任何事情都要看投入产出比,对应到数据库上其实就是性价比。POLARDB作为一款阿里自研数据库,经常被问的问题是:性能怎么样?能不能支撑我的业务?价格贵不贵?很显然,在早期调研阶段&#xff…

38年后的今天,用数据回顾什么是女排精神?

戳蓝字“CSDN云计算”关注我们哦!作者 | 朱小五责编 | 阿秃每当我们足篮打水一场空时,总会想起女排。38年前,1981年11月16日,中国队击败当时号称“东洋魔女“的日本女排,首次夺得世界冠军,这也是中国在三大…

阿里如何将“高峰前扩容、高峰后缩容”的梦想照进现实?

一、2017年我们做了什么? 记得早在2017年的时候,王坚博士就曾召大家就关于“IDC As a Computer”是否能做到,进行过激烈的讨论。而要做到此,必须要实现存储计算分离,分离后由调度对计算和存储资源进行独立自由调度。而…

阿里云异构计算团队亮相英伟达2018 GTC大会

1、首届云原生计算国际会议上,弹性计算研究员伯瑜介绍了基于虚拟化、容器化编排技术的云计算操作系统PouchContainer 首届云原生计算国际会议(KubeCon CloudNativeCon,China,2018)在上海举办,弹性计算研究…

发布国内首个无服务器容器服务,运维效率从未如此高效

近年来,随着越来越多的企业基于微服务架构构建自身核心业务平台后,微服务已获得越来越多技术人员的肯定,同时,微服务也承载着企业数字化转型的重任。但微服务架构的落地给企业的运维团队带来了不少的挑战,原有的运维方…

OPPO 正式发布 ColorOS 7,“轻”装上阵带来多项亮眼新功能……

今日OPPO举办了 ColorOS 7 发布会,正式发布了主打“轻快无边界”的ColorOS 7,以“轻”为核心构建了无边界设计 2.0,在UI、交互、动画、声效等方面进行了优化,围绕“快”阐述了ColorOS 7 在畅快使用、高效体验上所做的创新功能。 此…

ubuntu mysql5.6_ubuntu安装mysql5.6

安装mysql5.6在ubuntu上安装mysql5.6的版本1.添加mysql5.6的源sudo apt-get install software-properties-commonsudo add-apt-repository deb http://archive.ubuntu.com/ubuntu trusty universe2.安装mysql5.6sudo apt-getupdatesudo apt install mysql-server-5.6sudo apt i…

流量隔离方案 Dpath 护航双十一新零售

需求 在今年的双11准备期间,业务同学提出要针对新零售进行特殊的保障,希望新零售过来的流量,单独进入到一批机器,和其他普通流量隔离开来,这对新零售系统稳定性提出更高的要求。 需求总结下来就是: 针对…

潘石屹接连带货 Python,要来抢我们的饭碗?

地产大亨潘石屹近日开始一项全新学习,刷爆程序员的朋友圈,程序员纷纷议论起来:地产大亨也要来抢饭碗啦?11月14日,他说:今天开始我学习一门新的语言Python,我在寻找。这也是今天给自己人生的礼物…

mysql连接不上怎么重置密码错误_MySQL数据库连接不上、密码修改问题

1.问题描述版本:【CentOS8】【MySQL5.7】问题:远程使用navicat连接不上数据库,后来试了在linux中都登不进去。根据提示应该是密码不对,此文主要介绍密码修改和一些注意事项。2.解决方法1)因为现在登不进去,所以没法修改…

服务化改造的云上利器 | 阿里云 EDAS 重大升级发布

11月22日,广东云栖大会企业级互联网架构专场上,阿里云发布了全新版本的企业级分布式应用服务EDAS。 新版本增强了对主流微服务框架的原生支持,实现SpringCloud & Dubbo用户代码零侵入就能迁移至EDAS,降低开发者的接入门槛&am…

Nacos发布0.5.0版本,轻松玩转动态 DNS 服务

阿里巴巴微服务开源项目Nacos于近期发布v0.5.0版本,该版本主要包括了DNS-basedService Discovery,对Java 11的支持,持续优化Nacos产品用户体验,更深度的与Spring Cloud体系的网关集成等方面做了演进。 一、发布 DNS-F 为了进一步…