简述Java测试的自定义断言

对于测试来说,编写断言似乎很简单:我们只需要对结果和预期进行比较,通常使用断言方法进行判断,例如测试框架提供的assertTrue()或者assertEquals()方法。然而,对于更复杂的测试场景,使用这些基础的断言验证结果可能会显得相当笨拙。

使用这些基础断言的主要问题是,底层细节掩盖了测试本身,这是我们不希望看到的。在我看来,应该争取让这些测试使用业务语言来说话。

在本篇文章中,我将展示如何使用“匹配器类库”(matcher library);来实现自定义断言,从而提高测试代码的可读性和可维护性。

为了方便演示,我们假设有这样一个任务:让我们想象一下,我们需要为应用系统的报表模块开发一个类,输入两个日期(开始日期和结束日期),这个类将给出这两个日期之间所有的每小时间隔。然后使用这些间隔从数据库查询所需数据,并以直观的图表方式展现给最终用户。

标准方法

我们先采用“标准”的方法来编写断言。我们以JUnit为例,当然你也可以使用TestNG。我们将使用像assertTrue()、assertNotNull()或assertSame()这样的断言方法。

下面展示了HourRangeTest类的其中一个测试方法。它非常简单。首先调用getRanges()方法,得到两个日期之间所有的每小时范围。然后验证返回的范围是否正确。

private final static SimpleDateFormat SDF
        = new SimpleDateFormat("yyyy-MM-dd HH:mm");
@Test
public void shouldReturnHourlyRanges() throws ParseException {
       // given
       Date dateFrom = SDF.parse("2012-07-23 12:00");
       Date dateTo = SDF.parse("2012-07-23 15:00");
       // when
       final List<range> ranges = HourlyRange.getRanges(dateFrom, dateTo);
       // then
       assertEquals(3, ranges.size());
       assertEquals(SDF.parse("2012-07-23 12:00").getTime(), ranges.get(0).getStart());
       assertEquals(SDF.parse("2012-07-23 13:00").getTime(), ranges.get(0).getEnd());
       assertEquals(SDF.parse("2012-07-23 13:00").getTime(), ranges.get(1).getStart());
       assertEquals(SDF.parse("2012-07-23 14:00").getTime(), ranges.get(1).getEnd());
       assertEquals(SDF.parse("2012-07-23 14:00").getTime(), ranges.get(2).getStart());
       assertEquals(SDF.parse("2012-07-23 15:00").getTime(), ranges.get(2).getEnd());
}

毫无疑问这是个有效的测试。然而,它有个严重的缺点。在//then后面有大量的重复代码。显然,它们是复制和粘贴的代码,经验告诉我,它们将不可避免地会产生错误。此外,如果我们写更多类似的测试(我们肯定还要写更多的测试来验证HourlyRange类),同样的断言声明将在每一个测试中不断地重复。

过多的断言和每个断言的复杂性减弱了当前测试的可读性。大量的底层噪音使我们无法快速准确地了解这些测试的核心场景。我们都知道,阅读代码的次数远大于编写的次数(我认为这同样适用于测试代码),所以我们理所当然地要想办法提高其可读性。

在我们重写这些测试之前,我还想重点说一下它的另一个缺点,这与错误信息有关。例如,如果getRanges()方法返回的其中一个Range与预期不同,我们将得到类似这样的信息:

org.junit.ComparisonFailure:
Expected :1343044800000
Actual :1343041200000

这些信息太不清晰,理应得到改善。

私有方法

那么,我们究竟能做些什么呢?好吧,最显而易见的办法是将断言抽成一个私有方法:

private void assertThatRangeExists(List<Range> ranges, int rangeNb,
                                   String start, String stop) throws ParseException {
        assertEquals(ranges.get(rangeNb).getStart(), SDF.parse(start).getTime());
        assertEquals(ranges.get(rangeNb).getEnd(), SDF.parse(stop).getTime());
}
@Test
public void shouldReturnHourlyRanges() throws ParseException {
       // given
       Date dateFrom = SDF.parse("2012-07-23 12:00");
       Date dateTo = SDF.parse("2012-07-23 15:00");
       // when
       final List<Range> ranges = HourlyRange.getRanges(dateFrom, dateTo);
       // then
       assertEquals(ranges.size(), 3);
       assertThatRangeExists(ranges, 0, "2012-07-23 12:00", "2012-07-23 13:00");
       assertThatRangeExists(ranges, 1, "2012-07-23 13:00", "2012-07-23 14:00");
       assertThatRangeExists(ranges, 2, "2012-07-23 14:00", "2012-07-23 15:00");
}

这样是不是好些?我会说是的。减少了重复代码的数量,提高了可读性,这当然是件好事。

这种方法的另一个优势是,我们现在可以更容易地改善验证失败时的错误信息。因为断言代码被抽到了一个方法中,所以我们可以改善断言,很容易地提供更可读的错误信息。

为了更好地复用这些断言方法,可以将它们放到测试类的基类中。

不过,我觉得我们也许能做得更好:使用私有方法也有缺点,随着测试代码的增长,很多测试方法都将使用这些私有方法,其缺点将更加明显:

断言方法的命名很难清晰反映其校验的内容。

随着需求的增长,这些方法将会趋向于接收更多的参数,以满足更复杂检查的要求。(assertThatRangeExists()现在有4个参数,已经太多了!)

有时候,为了在多个测试中复用这些代码,会在这些方法中引入一些复杂逻辑(通常以布尔标志的形式校验它们,或在某些特殊的情况下,忽略它们)。

从长远来看,所有使用私有断言方法编写的测试,意味着在可读性和可维护性方面将会遇到一些问题。我们来看一下另外一种没有这些缺点的解决方案。

查看本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/Java/

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索测试
, 方法
, 代码
, parse
, sdf
, 2012
可读性
自动化测试 断言 java、java 单元测试 断言、java单元测试和断言、java 断言、java 断言assert,以便于您获取更多的相关知识。

时间: 2024-11-03 06:06:05

简述Java测试的自定义断言的相关文章

《Java数字图像处理:编程技巧与应用实践》——1.3 用Java Swing绘制自定义的JPanel

1.3 用Java Swing绘制自定义的JPanel Swing的JPanel组件是GUI编程中最重要的面板组件,可以通过重写JPanel中paint-Component方法实现对JPanel面板组件的背景颜色的调整或添加背景图片,进而实现自定义版本的面板(JPanel)组件.只要完成如下几步就可以实现一个简单自定义JPanel面板的绘制. 1)实现对JPanel面板的继承,代码如下: public class CustomJPanel extends JPanel { // 更多代码 } 2

如何使用Java测试网络连通性

概述 在网络编程中,有时我们需要判断两台机器之间的连通性,或者说是一台机器到另一台机器的网络可达性.在系统层面 的测试中,我们常常用 Ping 命令来做验证.尽管 Java 提供了比较丰富的网络编程类库(包括在应用层的基于 URL 的网 络资源读取,基于 TCP/IP 层的 Socket 编程,以及一些辅助的类库),但是没有直接提供类似 Ping 命令来测试网络连通 性的方法.本文将介绍如何通过 Java 已有的 API,编程实现各种场景下两台机器之间的网络可达性判断.在下面的章节中 ,我们会使

Java测试包Mockito

Java测试包Mockito 记录一些基本功能,Mockito主要是用于为测试提供Mock. 生成Mock,代替目标对象 List<String> list = mock(List.class); 目标对象方法的返回结果 我们要让mock对象的方法,按照我们的想法返回结果.使用when().then()语法定义. when(list.get(anyInt())).then(new Answer<String>(){ @Override public String answer(In

spring mvc-Sprin-test测试Controller,断言报错:No ModelAndView found

问题描述 Sprin-test测试Controller,断言报错:No ModelAndView found 问题很奇怪: 使用MockMvc测试SpringMVC的Controller层,调用mockMvc.perform(MockMvcRequestBuilders.post("XXXX")的时候无法调用到Controlelr层的代码,且只要XXX是"/xxx"格式的,返回的状态就一直是200,但结果永远都是null. 1.controller注册了的,我检查了

关于 java 正则表达式 零宽断言 求回答

问题描述 关于 java 正则表达式 零宽断言 求回答 String= >Symantec bbSEPM码.a>display>"itemcca> regex= (?<=>).*?a> 问:能匹配到什么? 我认为是:Symantec bbSEPM码.a> "itemcca> 但结果是:Symantec bbSEPM码.a> "display>"itemcca> 不能理解!求大神!

基于Java Socket的自定义协议,实现Android与服务器的长连接(一)

一.基础知识准备 在正式给大家介绍自定义协议之前,我们先对网络传输和协议解析的相关知识点做一个基本的介绍,尽管这些知识点我们在学校里学过,但难免会有所遗忘,这里先做一个简单的介绍,以便对后文的内容理解更加顺畅. 1. 网络七层协议 OSI的7层从上到下分别是:7 应用层. 6 表示层. 5 会话层. 4 传输层. 3 网络层. 2 数据链路层. 1 物理层;其中高层(即7.6.5.4层)定义了应用程序的功能,下面3层(即3.2.1层)主要面向通过网络的端到端的数据流.应用层常见的协议有:HTTP

Java 测试URL地址是否能正常连接的代码_java

Java 测试URL地址是否能正常连接 public static int testWsdlConnection(String address) throws Exception { int status = 404; try { URL urlObj = new URL(address); HttpURLConnection oc = (HttpURLConnection) urlObj.openConnection(); oc.setUseCaches(false); oc.setConne

Java测试覆盖率工具----Cobertura,EclEmma

Cobertura 是一个与Junit集成的代码覆盖率测量工具 它是免费.开源的 它可以与Ant和Maven集成,也可以通过命令行调用 可以生成HTML或XML格式的报告 可以按照不同的标准对HTML结果进行排序 为每个类.包以及整个项目计算所覆盖的代码行与代码分支的百分比例 原创文章,版权所有,允许转载,标明出处:http://blog.csdn.net/wanghantong Eclipse插件地址: http://ecobertura.johoop.de/update/ (requires

http-最近闲着无聊用java测试了Http的发送流程

问题描述 最近闲着无聊用java测试了Http的发送流程 最近闲着无聊用java测试了Http的发送流程,发现请求的头和正文是通过空行分割的,但是通道一直打开,无法判断是否已经读完了,但是Servlet中的输入流的read()是能返回-1的.求指导如何判断流已经结束了,重新封装IputStream也是如要知道何时结束的. 解决方案 http头读到空白行结束,然后实体部分根据HTTP头的length进行读取