初级开发人员在编写单元测试时常犯的错误

自从我编写第一个单元测试以来已经有10年了。 从那时起,我不记得我已经编写了成千上万的单元测试。 老实说,我在源代码和测试代码之间没有任何区别。 对我来说是同一回事。 测试代码是源代码的一部分。 在过去的3-4年中,我与多个开发团队合作,并且有机会查看了大量的测试代码。 在这篇文章中,我总结了经验不足的开发人员在编写单元测试时通常会犯的最常见错误。

让我们看一下以下简单的类示例,该类收集注册数据,对其进行验证并执行用户注册。 显然,该方法非常简单,其目的是演示单元测试的常见错误,而不是提供功能齐全的注册示例:

public class RegistrationForm {private String name,email,pwd,pwdVerification;// Setters - Getters are ommitted public boolean register(){validate();return doRegister();}private void validate () {check(name, "email");check(email, "email");check(pwd, "email");check(pwdVerification, "email");if (!email.contains("@")) {throw new ValidationException(name + " cannot be empty.");} if ( !pwd.equals(pwdVerification))throw new ValidationException("Passwords do not match.");}private void check(String value, String name) throws ValidationException {if ( value == null) {throw new ValidationException(name + " cannot be empty.");}if (value.length() == 0) {throw new ValidationException(name + " is too short.");}}private boolean doRegister() {//Do something with the persistent contextreturn true;}

这是注册方法的相应单元测试,有意显示单元测试中最常见的错误。 实际上,我已经看过很多次非常相似的测试代码,所以这不是我所说的科幻小说:

@Testpublic void test_register(){RegistrationForm form = new RegistrationForm();form.setEmail("Al.Pacino@example.com");form.setName("Al Pacino");form.setPwd("GodFather");form.setPwdVerification("GodFather");assertNotNull(form.getEmail());assertNotNull(form.getName());assertNotNull(form.getPwd());assertNotNull(form.getPwdVerification());form.register();}

测试达斯·维德

现在,此测试显然将通过,开发人员将看到绿灯,所以竖起大拇指! 让我们转到下一个方法。 但是,此测试代码有几个重要问题。

在我的拙见中,第一个是单元测试的最大误用是测试代码没有充分测试寄存器方法。 实际上,它仅测试许多可能路径中的一个。 我们确定该方法将正确处理空参数吗? 如果电子邮件中不包含@字符或密码不匹配,该方法将如何工作? 开发人员倾向于只为成功的路径编写单元测试,而我的经验表明,代码中发现的大多数错误都与成功的路径无关。 一个非常好的规则要记住的是,对于每一个方法,你需要N个测试,其中N等于在圈复杂度将所有私有方法调用的圈复杂度的方法。

接下来是测试方法的名称。 为此,我部分归咎于所有这些现代IDE,它们自动为测试方法(如示例中的方法)生成愚蠢的名称。 测试方法的命名应向读者解释将要测试的内容和条件 。 换句话说,它应该描述正在测试的路径。 在我们的情况下,更好的名称可以是: should_register_when_all_registration_data_are_valid。 在本文中,您可以找到几种命名单元测试的方法,但是对我来说,“应该”模式最接近人类语言,并且在阅读测试代码时更容易理解。

现在,让我们看一下代码的内容。 有几个断言,这违反了每个测试方法应断言一件事的规则 。 此声明四(4)个RegistrationForm属性的状态。 这使测试更难以维护和阅读(哦,是的,测试代码应该像源代码一样可维护和可读。请记住,对我而言它们之间没有区别),并且很难理解测试的哪一部分失败。

此测试代码还声明了setter / getter。 这真的有必要吗? 为了回答这个问题,我将引用罗伊·奥什罗夫(Roy Osherove)的名言:“ 单元测试的艺术 ”

属性(Java中的获取器/设置器)是很好的示例代码,通常不包含任何逻辑,并且不需要测试。 但是要当心:在属性中添加任何检查后,您将要确保逻辑已经过测试。

在我们的案例中,设置器/获取器中没有业务逻辑,因此这些断言完全没有用。 此外,他们错了,因为他们甚至没有测试安装员的正确性。 想象一下,一个邪恶的开发人员将getEmail方法的代码更改为始终返回常量String而不是email属性值。 该测试仍将通过,因为它断言setter不为null,并且未断言期望值。 因此,这可能是您要记住的一条规则。 断言方法的返回值时,请始终尝试尽可能具体 。 换句话说,除非您不关心实际的返回值,否则请尽量避免使用assertIsNull,assertIsNotNull。

我们正在查看的测试代码的最后但并非最不重要的问题是,从未断言正在测试的实际方法( 寄存器 )。 它在测试方法内部被调用,但是我们从不评估其结果。 这种反模式的变化甚至更糟。 在测试用例中甚至不会调用被测方法。 因此,请记住, 您不仅应调用被测方法,而且还应始终声明预期结果,即使它只是一个布尔值 。 有人可能会问:“无效方法是什么?”。 好的问题,但这是另一次讨论–可能是另一篇文章,但是为您提供一些提示,测试void方法可能会掩盖不好的设计,或者应该使用验证方法调用的框架(例如Mockito.Verify )来完成

作为奖励,您应该记住这是一条最终规则。 想象一下, doRegister实际上已实现,并且对外部数据库做了一些实际的工作。 如果某个本地环境中未安装数据库的开发人员尝试运行测试,将会发生什么情况。 正确! 一切都会失败。 确保测试即使从仅可访问代码和JDK的最笨拙的终端运行,也将具有相同的行为 。 没有网络,没有服务,没有数据库,没有文件系统。 没有!

翻译自: https://www.javacodegeeks.com/2014/09/common-mistakes-junior-developers-do-when-writing-unit-tests.html

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

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

相关文章

OpenDaylight开发hello-world项目之开发工具安装

OpenDaylight开发hello-world项目之开发环境搭建 OpenDaylight开发hello-world项目之开发工具安装 OpenDaylight开发hello-world项目之代码框架搭建 在ODL开发之前,要安装好开发环境。ODL使用java语言开发,所以要安装好java。ODL的代码框架是有maven这个…

Google Chrome 扩展程序开发

根据公司的规定,每月八小时,弹性工作制。所以大家平时来的不太准时,如果有事,下班也就早些回去了。所以一个月下来工作时间可能不够,但是公司的考勤日历是这样的: 除了请假和法定节假日外,其他样…

[Silverlight入门系列]使用MVVM模式(7):ViewModel的INotifyPropertyChanged接口实现

本文说说ViewModel的这个INotifyPropertyChanged接口可以用来做啥? 举例1:我有个TabControl,里面放了很多View,每个由ViewModel控制,我想是想TabSelectionChanged就打开相应的ViewModel,怎么做?…

[读书笔记]TCP/IP详解V1读书笔记-4 5

IP地址与以太网地址之间的关系 R P发送一份称作A R P请求的以太网数据帧给以太网上的每个主机。这个过程称作广播,在32 bit的I P地址和采用不同网络技术的硬件地址之间提供动态映射 ----------------------------------------- arp以太网帧的类型字段为x 0 8 0 6&am…

未来是Apache Karaf上的微服务架构

这是Jamie Goodyear的客座博客文章( 博客 , icbts )。 他是Savoir Technologies的开源倡导者,Apache开发人员和计算机系统分析师; 他为全球大型组织设计,批判和支持了体系结构。 他拥有纽芬兰纪念大学的计…

使用内存回流的方法来实现将image的内容转换为 byte[]

在今天的开发中老大不知道怎么突发奇想&#xff0c;要使用Image的Byte数据。当时使用老几种方式效果均不理想&#xff0c;最后发现其实可以使用内存回流的方式来实现。多的不说老&#xff0c;马上贴上代码&#xff1a;/**//// <summary> /// 将byte[]转换为Image…

通过设计国际象棋游戏来了解策略模式

今天&#xff0c;我们将借助一个示例来尝试了解策略模式。 我们将考虑的示例是国际象棋游戏。 这里的目的是解释策略模式&#xff0c;而不是构建全面的国际象棋游戏解决方案。 策略模式&#xff1a;策略模式被称为行为模式-用于管理对象之间的算法&#xff0c;关系和职责。 策…

群发邮件

最近&#xff0c;通过两周的学习&#xff0c;对.net 的基础知识有了进一步的了解。觉得自己可以写个小程序了。于是花了两天时间写了一个 群发邮件的一个WinForm小程序。自己在这里小秀一下&#xff0c;表扬及鼓励一下自己。哈哈&#xff01; 此小程序在发送邮件的基础上还添加…

Npm install failed with “cannot run in wd”

Linux环境下&#xff0c;root账户&#xff0c;安装某些npm包的时候报下面的错误&#xff0c;例如安装grunt-contrib-imagemin时&#xff1a; Error: EACCES, mkdir /usr/local/lib/node_modules/coffee-scriptnpm ERR! { [Error: EACCES, mkdir /usr/local/lib/node_modules/c…

我的Google Adsense帐户被关

一、 上周四&#xff0c;我收到Google的邮件&#xff0c;宣布关闭我的Adsense帐户。 "您好&#xff01; 查看了相关记录后&#xff0c;我们确认您的 AdSense 帐户存在引起无效活动的风险。保护 AdWords 广告客户&#xff0c;使其免受无效活动的侵害是我们的责任&#xff0…

csharp: ODP.NET,System.Data.OracleClient(.net 4.0) and System.Data.OleDb读取Oracle g 11.2.0的区别...

ODP.NET: 引用&#xff1a; using Oracle.DataAccess; //Oracle g 11.2.0 using Oracle.DataAccess.Client; using Oracle.DataAccess.Types; //下载 http://www.oracle.com/technetwork/topics/dotnet/downloads/net-downloads-160392.html //引用&#xff1a;D:\app\geovindu…

Java 9幕后花絮:新功能从何而来?

找出Java幕后发生的事情&#xff0c;以及新功能如何实现 在上一篇文章中&#xff0c;我们介绍了即将发布的Java 9版本的新功能和尚待解决的功能&#xff0c;并简要提到了将新功能添加到下一个版本之前要经历的过程。 由于此过程几乎影响了所有Java开发人员&#xff0c;但大多数…

sudo apt-get install libstdc++6

sudo apt-get install libstdc6 yum install libncurses.so.5 sudo apt-get install libncurses.so.5 sudo apt-get install lib32ncurses5 apt-get update把源更新一下 使用gdb时的指令 (gbd) info line *0x08xxxx sudo apt-get install lib32z1 lib32ncurses5 lib32bz2-1.…

AngularJS快速入门指南03:表达式

AngularJS通过表达式将数据绑定到HTML。 AngularJS表达式 AngularJS表达式写在双大括号中&#xff1a;{{ 表达式语句 }}。 AngularJS表达式绑定数据到HTML的方式与ng-bind指令的方式相同。 AngularJS会准确地将表达式“输出”为计算的结果。 AngularJS表达式与JavaScript表达式…

零基础快速上手HarmonyOS ArkTS开发2---ArkTS开发实践

ArkTS开发实践&#xff1a; 接着上一次零基础快速上手HarmonyOS ArkTS开发1---运行Hello World、ArkTS开发语言介绍继续&#xff0c; 在上一次对于ArkTS的基础知识进行了学习&#xff0c;依照官方的课程计划&#xff0c;还有两个具体的小案例需要来实践实践&#xff1a; 实践出…

八、VueJs 填坑日记之参数传递及内容页面的开发

我们在上一篇博文中&#xff0c;渲染出来了一个列表&#xff0c;并在列表中使用了router-link标签&#xff0c;标签内的&#xff1a;to就是链接地址&#xff0c;昨天咱们是<router-link :to"/content/ i.id">这样写的&#xff0c;今天我们来完成内容页面的渲染…

为Kindeditor控件添加图片自动上传功能

Kindeditor是一款功能强大的开源在线HTML编辑器&#xff0c;支持所见即所得的编辑效果。它使用JavaScript编写&#xff0c;可以无缝地与多个不同的语言环境进行集成&#xff0c;如.NET、PHP、ASP、Java等。官方网站可以查看这里&#xff1a;http://kindeditor.net/index.php Ki…

4个万无一失的技巧让您开始使用JBoss BRMS 6.0.3

上周&#xff0c;红帽发布了标记为6.0.3的JBoss BRMS的下一版本&#xff0c;已订阅的用户可以在其客户门户中使用。 如果您对该版本的新增功能感到好奇&#xff0c;请在客户门户网站上在线查看版本说明和其余文档 。 我们正在寻找一些简单的方法来开始使用此新版本&#xff0…

带有Angular JS的Java EE 7 – CRUD,REST,验证–第2部分

这是Angular JS承诺的Java EE 7的后续版本–第1部分 。 花了比我预期更长的时间&#xff08;找到时间来准备代码和博客文章&#xff09;&#xff0c;但是终于到了&#xff01; 应用程序 第1部分中的原始应用程序只是带有分页的简单列表&#xff0c;以及提供列表数据的REST服务…

Chrome不显示OPTIONS请求的解决方法2021版chrome90

在chrome90上之前展示跨域请求预检请求的方法失效了&#xff1a; 在chrome地址栏总输入 chrome://flags/#out-of-blink-cors 将其设置为Disabled后重启浏览器 在chrome://flags找不到选项out-of-blink-cors。取而代之的是chrome将预检请求放到了控制台网络面板的 OTHER 面板中。…