赞扬精心设计:基于属性的测试如何帮助我成为更好的开发人员

开发人员的测试工具箱就是其中之一,很少保持不变。 可以肯定的是,某些测试实践已被证明比其他测试更有价值,但是,我们仍在不断寻找更好,更快和更具表现力的方法来测试我们的代码。 基于属性的测试 是 Java社区中鲜为人知的 ,这是Haskell员工精心制作的另一种瑰宝,并在QuickCheck论文中进行了介绍 。

Scala社区(诞生了ScalaCheck库的Scala社区)和许多其他组织很快就意识到了这种测试技术的强大功能,但是Java生态系统在相当长的一段时间内一直缺乏采用基于属性的测试的兴趣。 幸运的是,自从jqwik出现以来,情况就在慢慢变化, 以求更好。

对于许多人来说,很难掌握什么是基于属性的测试以及如何利用它。 杰西卡·克尔 ( Jessica Kerr)的精彩演讲《基于属性的测试,更好的代码》 ,以及有关基于属性的测试的简介,基于 属性的测试模式的系列文章,是吸引您的绝佳来源,但是在今天的帖子中,我们将尝试发现典型的Java开发人员使用jqwik进行的基于属性的测试的实践方面。

首先, 基于属性的测试名称实际上意味着什么? 每个Java开发人员的第一个想法都会是它旨在测试所有的getter和setter(您好100%覆盖率)吗? 并非如此,尽管对于某些数据结构而言可能很有用。 相反,我们应该确定组件,数据结构甚至单个功能的高级特性 ,并通过提出假设有效地对其进行检验。

我们的第一个示例属于“那里又回来”类别:将序列化和反序列化为JSON表示形式。 被测试的类是User POJO ,尽管很简单,但请注意它具有OffsetDateTime类型的一个时间属性。

 public class User { private String username; @JsonFormat (pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS[SSS]]XXX" , shape = Shape.STRING) private OffsetDateTime created;     // ...  } 

令人惊讶的是,由于每个人都尝试使用自己的表示形式,因此如今使用日期/时间属性进行操作的频率有多少会引起问题。 如您所见,我们的合同使用的是ISO-8601互换格式,带有可选的毫秒部分。 我们要确保的是,可以将任何有效的User实例序列化为JSON ,然后反序列化为Java对象,而不会失去任何日期/时间精度。 作为练习,让我们首先尝试用伪代码来表达它:

 For any user Serialize user instance to JSON Deserialize user instance back from JSON Two user instances must be identical 

看起来很简单,但是令人惊讶的部分出在这里:让我们看看如何使用jqwik库将此伪代码投影到实际的测试用例中。 它尽可能地接近我们的伪代码。

 @Property  void @ForAll serdes( @ForAll ( "users" ) User user) throws JsonProcessingException { final String json = serdes.serialize(user); assertThat(serdes.deserialize(json)) .satisfies(other -> { assertThat(user.getUsername()).isEqualTo(other.getUsername()); assertThat(user.getCreated().isEqual(other.getCreated())).isTrue(); });         Statistics.collect(user.getCreated().getOffset());  } 

测试用例读起来非常简单,通常是自然的,但是显然, jqwik的@Property@ForAll批注后面隐藏着一些背景。 让我们从@ForAll开始,并清除所有这些User实例的来源。 您可能猜到了,这些实例必须生成,最好以随机方式生成。

对于大多数内置数据类型, jqwik具有一组丰富的数据提供程序( Arbitraries ),但是由于我们要处理特定于应用程序的类,因此我们必须提供自己的生成策略。 它应该能够发出具有广泛的用户名以及不同时区和偏移量集合的日期/时刻的User类实例。 首先让我们先略过提供者的实现,然后再详细讨论。

 @Provide  Arbitrary<User> users() { final Arbitrary<String> usernames = Arbitraries.strings().alpha().ofMaxLength( 64 );  final Arbitrary<OffsetDateTime> dates = Arbitraries .of(List.copyOf(ZoneId.getAvailableZoneIds())) .flatMap(zone -> Arbitraries .longs() .between(1266258398000L, 1897410427000L) // ~ +/- 10 years .unique() .map(epochMilli -> Instant.ofEpochMilli(epochMilli)) .map(instant -> OffsetDateTime.from(instant.atZone(ZoneId.of(zone))))); return Combinators .combine(usernames, dates) .as((username, created) -> new User(username).created(created));  } 

用户名的来源很简单:只是随机字符串。 日期来源基本上可以是2010年到2030年之间的任何日期/时间,而时区部分(因此是偏移量)是从所有可用的基于区域的区域标识符中随机选择的。 例如,下面是jqwik提出的一些示例。

 { "username" : "zrAazzaDZ" , "created" : "2020-05-06T01:36:07.496496+03:00" }  { "username" : "AZztZaZZWAaNaqagPLzZiz" , "created" : "2023-03-20T00:48:22.737737+08:00" }  { "username" : "aazGZZzaoAAEAGZUIzaaDEm" , "created" : "2019-03-12T08:22:12.658658+04:00" }  { "username" : "Ezw" , "created" : "2011-10-28T08:07:33.542542Z" }  { "username" : "AFaAzaOLAZOjsZqlaZZixZaZzyZzxrda" , "created" : "2022-07-09T14:04:20.849849+02:00" }  { "username" : "aaYeZzkhAzAazJ" , "created" : "2016-07-22T22:20:25.162162+06:00" }  { "username" : "BzkoNGzBcaWcrDaaazzCZAaaPd" , "created" : "2020-08-12T22:23:56.902902+08:45" }  { "username" : "MazNzaTZZAEhXoz" , "created" : "2027-09-26T17:12:34.872872+11:00" }  { "username" : "zqZzZYamO" , "created" : "2023-01-10T03:16:41.879879-03:00" }  { "username" : "GaaUazzldqGJZsqksRZuaNAqzANLAAlj" , "created" : "2015-03-19T04:16:24.098098Z" }  ... 

默认情况下, jqwik将针对1000套不同的参数值(随机用户实例)运行测试。 非常有用的“ 统计”容器允许收集您好奇的任何分布见解。 以防万一,为什么不按区域偏移量收集分布呢?

 ... - 04 : 00 ( 94 ) : 9.40 % - 03 : 00 ( 76 ) : 7.60 % + 02 : 00 ( 75 ) : 7.50 % - 05 : 00 ( 74 ) : 7.40 % + 01 : 00 ( 72 ) : 7.20 % + 03 : 00 ( 69 ) : 6.90 % Z     ( 62 ) : 6.20 % - 06 : 00 ( 54 ) : 5.40 % + 11 : 00 ( 42 ) : 4.20 % - 07 : 00 ( 39 ) : 3.90 % + 08 : 00 ( 37 ) : 3.70 % + 07 : 00 ( 34 ) : 3.40 % + 10 : 00 ( 34 ) : 3.40 % + 06 : 00 ( 26 ) : 2.60 % + 12 : 00 ( 23 ) : 2.30 % + 05 : 00 ( 23 ) : 2.30 % - 08 : 00 ( 20 ) : 2.00 % ... 

让我们考虑另一个例子。 想象一下,我们决定基于用户名属性重新实现User类的相等性(在Java中,这意味着重写equalshashCode )。 这样,对于任何一对User类实例,以下不变量必须为true:

  • 如果两个User实例具有相同的用户名 ,则它们相等,并且必须具有相同的哈希码
  • 如果两个User实例具有不同的username ,则它们不相等(但哈希码不一定相同)

它非常适合基于属性的测试,并且jqwik尤其使这种测试的编写和维护变得微不足道。

 @Provide  Arbitrary&ltString> usernames() { return Arbitraries.strings().alpha().ofMaxLength( 64 );  }  @Property  void equals( @ForAll ( "usernames" ) String username, @ForAll ( "usernames" ) String other) { Assume.that(!username.equals(other));         assertThat( new User(username)) .isEqualTo( new User(username)) .isNotEqualTo( new User(other)) .extracting(User::hashCode) .isEqualTo( new User(username).hashCode());  } 

由于我们引入了用户名的两个来源,因此通过假设表达的假设允许对生成的参数施加额外的约束,这可能会发生,因为它们在同一运行中都发出相同的用户名,因此测试将失败。

您到目前为止可能遇到的问题是:重点是什么? 在不进行基于属性的测试和使用jqwik的情况下 ,肯定可以测试序列化/反序列化或equals / hashCode ,那么为什么还要麻烦呢? 足够公平,但是这个问题的答案基本上取决于我们如何进行软件系统设计。

总的来说, 基于属性的测试在很大程度上受函数式编程的影响,这并不是Java的第一件事(至少现在还不是),这要轻描淡写地说。 随机生成测试数据本身并不是一个新颖的主意,但是至少在我看来, 基于属性的测试鼓励您去做的是,以更抽象的角度思考,而不是专注于单个操作(等于,比较,加法)。 ,排序,序列化...),但是它们要遵循什么样的属性,特征,定律和/或不变式。 当然,这感觉就像是一种外来技术,如果您愿意,可以进行范式转换,鼓励您花更多的时间设计正确的东西。 这并不意味着从现在起您的所有测试都必须基于属性,但是我相信它当然应该在我们的测试工具箱的前排位置。

请在Github上找到完整的项目资源。

翻译自: https://www.javacodegeeks.com/2020/02/in-praise-of-the-thoughful-design-how-property-based-testing-helps-me-to-be-a-better-developer.html

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

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

相关文章

docker项目部署 php_「Docker部署PHP+Vue项目」- 海风纷飞Blog

创建Docker映射目录—— vue_demo # Demo项目—— php_vue—— docker-compose.yaml—— nginx———— apps # 项目代码———— conf # nginx配置文件—————— nginx.conf———— log # nginx———— vhost # 虚拟机配…

在美国本科 计算机排名2015,(word)2015年美国大学专业排名汇总-以计算机专业排名为例.doc...

(word)2015年美国大学专业排名汇总-以计算机专业排名为例美国大学经常有一些国内没有的专业&#xff0c;而且由于国情不同&#xff0c;很多在国内的热门专业&#xff0c;在国外可能不是那么“吃香”&#xff0c;另外不是名校的专业就是最好的&#xff0c;可能某个普通大学的专业…

stc单片机485发送多出一字节_单片机干货!STC8H案例制作分享(内含高清实物动图)...

本期&#xff0c;Lucy制作了九个案例分享给大家&#xff0c;分别为&#xff1a;流水灯、按键LED、数码管、点阵、定时蜂鸣器、NTC温度计、超声波测距仪、光敏RGB灯、氛围灯(红外)Lucy无偿提供全部案例的原理图和部分案例的代码。有需要的朋友先关注并私信我。需要源码私信我&am…

λ演算的语法和语义_λ和副作用

λ演算的语法和语义总览 Java 8添加了诸如lambda和类型推断之类的功能。 这使语言不再那么冗长和简洁&#xff0c;但是它带来了更多的副作用&#xff0c;因为您不必对自己的工作做得那么明确。 Lambda的返回类型很重要 Java 8推断闭包的类型。 一种方法是查看返回类型&#xf…

计算机系统中存储管理是,《计算机操作系统5、存储管理.doc

《计算机操作系统5、存储管理一、选择题1&#xff0e;存储器管理的主要功能是内存分配、地址映射、内存保护和( )。A&#xff0e;2&#xff0e;把逻辑地址转变为内存的物理地址的过程称作( )A&#xff0e; D&#xff0e;重定位3&#xff0e;物理地址对应的是( )。A&#xff0e;…

怎么调用获取被创建的预制体_Go 语言 Web 编程系列—— 获取用户请求数据(上)...

0、GET/POST 请求数据在 PHP 中&#xff0c;可以直接通过全局变量 $_GET 和 $_POST 快速获取 GET/POST 请求数据&#xff0c;GET 请求数据主要是 URL 查询字符串中包含的参数&#xff0c;以前面在线论坛项目的群组详情页为例&#xff1a;http://localhost:8080/thread/read?id…

Java 8 –集合sort()方法–按Employe对象(Id,名称,年龄)列出自定义排序示例...

有关在Java中对自定义对象进行排序的完整指南。 Collections.sort&#xff08;&#xff09;方法基于Comparable或Comparator实现进行排序。 用于对Employee对象进行排序的示例自定义排序 1.简介 在本教程中&#xff0c;您将学习如何在java中对Custom对象进行排序 。 首先&…

删除表报正在使用_U盘拔出要不要点quot;安全删除USB硬件quot;退出?

小U盘&#xff0c;大用处。U盘不仅可以用来存储各种各样的文件&#xff0c;甚至还可以用来制作电脑启动盘、Win to Go系统盘等。直接拔还是点“安全删除”后再拔U盘呢&#xff1f;在用完U盘后&#xff0c;有的会点击电脑右下角“安全删除”才拔&#xff0c;有的则会不管那么多直…

cpu性能测试软件 国际象棋,CPU性能评测软件

作者选择100电脑网推荐配置了解最佳配置看首页电脑CPU的评测软件有很多&#xff0c;一般用户用的鲁大师就是国内家喻户晓比较流行的评测软件&#xff0c;但是鲁大师的权威性一直没有被市场充分肯定。在windows系统出到vista后&#xff0c;windows内置了电脑性能评分&#xff0c…

java 参数命名冲突_Java中的命名参数

java 参数命名冲突创建具有许多参数的方法是一个主要的缺点。 每当需要创建这样的方法时&#xff0c;就在空气中闻一闻&#xff1a;这是代码的味道。 强化单元测试&#xff0c;然后进行重构。 没有借口&#xff0c;没有屁股。 重构&#xff01; 使用构建器模式&#xff0c;甚至…

react获取全局_使用react hooks实现的简单全局状态管理

注意,此代码存储库已被弃用,不会再更新维护了.Note that this code repository has been deprecated and will not be updated and maintained.react-simple-global-state-store-hook基于react hooks 和EventTarget 实现的极简全局状态管理 库&#xff0c;可以跨组件共享全局状…

职业规划测试软件,生涯规划常用测试工具

原标题&#xff1a;生涯规划常用测试工具认识自己测试生涯规划关于生涯规划的几个测试上周我们说到中学生很有必要进行生涯规划&#xff0c;在进行生涯规划前&#xff0c;我们需要对自己有一个更清晰、更全面的认识&#xff0c;随着心理学的发展&#xff0c;很多认识自我的测试…

使用SoapUI调用不同的安全WCF SOAP服务-基本身份验证,第一部分

在这个分为三部分的系列中&#xff0c;我将演示如何使用SoapUI API工具来调用安全的WCF SOAP服务。 第一篇文章将着重于创建将要测试的系统的服务。 第二篇文章将介绍在基本身份验证机制保护的情况下调用它所需的步骤。 在最后一部分中&#xff0c;我将对初始服务稍作更改&…

计算机动画分为关键帧动画和,一个最简单的动画最少有几个关键帧

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。一个最简单的动画至少有两个关键帧&#xff0c;两个关键帧中必须是元件&#xff0c;而且必须是同一个元件。要创建使组合体或文字发生颜色渐变的动画&#xff0c;必须先将它们转换为…

python+robotframework_python+robot framework接口自动化测试

转载&#xff1a;http://www.cnblogs.com/nzg-noway/p/6651957.htmlpythonrequests实现接口的请求前篇已经介绍&#xff0c;还有不懂或者疑问的可以访问目前我们需要考虑的是如何实现关键字驱动实现接口自动化输出&#xff0c;通过关键字的封装实现一定意义上的脚本与用例的脱离…

自动装箱和拆箱_自动装箱

自动装箱和拆箱自Java 1.5以来&#xff0c;所有Java开发人员都可以使用自动装箱功能。嗯&#xff0c;我可能太乐观了。 至少所有开发人员都应该可以使用自动装箱。 毕竟&#xff0c;在ORACLE页面上有一个很好的教程。 自动装箱是指Java编译器在需要时自动从原始类型创建用于创…

计算机网络标准体系,计算机网络标准体系结构实验报告.doc

华北电力大学实 验 报 告||试验名称 计算机网络体系结构试验课程名称 计算机网络体系结构||专业班级&#xff1a;网络1202 学生姓名&#xff1a;学 号&#xff1a; 成 绩&#xff1a;指导老师&#xff1a;李丽芬 试验日期&#xff1a;.12.18一、试验目标和要求1&#xff0e;将网…

配置中文_星球大战:战机中队配置需求公布 支持中文

近日《星球大战》系列新作《星球大战&#xff1a;战机中队》公布&#xff0c;该作采用寒霜引擎打造&#xff0c;支持中文。游戏将于2020年10月3日发售&#xff0c;预购价格为238元&#xff0c;登陆Xbox One/PS4/PC(Steam/Origin/Epic)平台&#xff0c;有单人和多人模式&#xf…

为什么在生产中进行硒自动化测试对于您的下一个版本至关重要?

您是否认为仅仅是因为您的Web应用程序在过渡环境中以鲜艳的色彩通过了&#xff0c;您的生产环境也将是相同的吗&#xff1f; 您可能需要重新考虑&#xff01; 特别是&#xff0c;如果我们指的是跨浏览器测试 &#xff0c;则需要确保跨各种操作系统&#xff0c;运行在不同操作系…

手机usb共享计算机网络连接,如何将手机wifi网络通过USB共享给电脑?小编教你共享方法...

曾经就有过这样的情况&#xff0c;家里突然断网了&#xff0c;这时又需要打开电脑接收文件&#xff0c;或是需要在线编辑公众号的文章&#xff0c;着急得很&#xff0c;这时电脑没有网络怎么办呢&#xff1f;能不能使用手机的流量&#xff0c;来让电脑连网呢&#xff1f;有时候…