java单元测试之junit之实战

1 编写该文章的起因

博主是一枚小新,经常挖坑填坑。最近在工作中遇到了这样一种情况。某天小伙伴说博主写得一个方法有问题,并且相应的测试类也跑不通。博主一直秉着磨刀不误砍柴工的思想,测试类都是写得好好地并且能杠杠运行的!怀着好奇,经过一番debug,发现某句代码抛出了空指针,如下

String url = linkedService.getUrlById(id);

getUrlById是通过id去查询数据库中的某条数据。问题到这里已经暴露无遗了,博主在测试该方法时将id写死,并且在数据库中能查出该条数据。而小伙伴的数据库中并没有这条数据,就导致了这个bug。最终,博主将sql语句发给小伙伴,测试通过了,问题似乎得到了**“解决”**,大家也皆大欢喜地继续做其他任务了。

然而,过了几天,博主在又遇到了相同的问题。

UserInfo user = userInfoService.getUser(id);

okok,这次博主一下子就定位到了问题的关键处,添加了相应的数据就解决了问题。但此时博主心里已经产生了一个疑问,并且在第二天例会时提了出来。

博主:“因为环境的改变(数据不同:每个同事维护自己的数据库,并没有使用共同的数据库),造成测试用例有时通过,有时不通过,这应该怎么有效的解决?”

孙大大:(博主的同事,喜欢专研问题并解决问题):“环境改变可能包括数据库,网络等其他因素,而你们遇到的这个问题,是测试用例写得不够自动化、专业化,在自己电脑上能测试并且通过,换到其他电脑上不能运行,这就是测试用例写得不够好。”

博主的好奇心一下子被吸引住了,如何解决这种问题,什么才能叫做写得好的测试用例?于是博主专门花了三天的时间阅读了 David Thomas 和Andrew Hunt 写的《单元测试之道Java版:使用JUnit》。这本书总共只有170多页,内容不多,没有啃大部头的那种挫败感,算是一本入门书籍,让我在短时间内了解如何使用JUnit编写单元测试。


2 如何编写好的测试类

2.1 运用好断言

一个单元测试是程序员写的一段代码,用于执行另一段代码并判断代码的行为是否与期望值一致。在实际中,为了验证行为和期望值是否一致,需要使用到assertion(断言)。它是一种非常简单的方法调用,用于判断某个语句是否为真。使用的时候需要在测试类中引入相应的方法

import static org.junit.Assert.*;

比如方法assertTrue将会检查给定的二元条件是否为真,如果条件非真,则该断言将会失败。具体的实现如下面所示:

public void assertTrue(boolean condition){
	if(!condition){
		abort();
	}
}

我们可以利用该断言来检查两个数字是否相等:

assertTrue(a == 2);

如果由于某种原因,当调用assertTrue()的时候,a并不等于2,那么上面的程序将会中止并报错。

2.2 少用输出语句

输出语句大家用的都不少,譬如现在要看一个pojo,在重写了它的toString()方法后,利用如下方式输出

 System.out.println(pojo);
 logger.info("pojo={}",pojo);

接着就在满是日志的控制台里查找我们需要的信息。这种方法并不是不可取,但是效率低。如果你已经知道了期望值,那么最好使用断言来判断结果。

2.3 注重有效的单元测试

本小节内容引用自《单元测试之道Java版:使用JUnit》

2.3.1 明确测试目的

我如何知道代码运行是否正确呢?
我要如何对它进行测试?
还有哪些方面可能会发生错误?
这个问题是否会在其他的地方出现呢?

2.3.2 一般原则

测试任务可能失败的地方。
测试任何已经失败的地方。
对于新加的代码,在被证明正确之前,都可能是有问题的。
至少编写和产品代码一样多的测试代码。
针对每次编译都做局部测试。
签入代码之前做全局测试。

2.3.3 使用你的RIGHT-BICEP

结果是否正确(Right)?
边界(boundary)条件是否正确?
是否可以检查反向(inverse)关联?
是否可以使用其他方法来跨检查(cross-check)结果?
错误条件(error condition)是否可以重新?
性能方面是否满足条件?

2.3.4 好的测试是一个TRIP

Automatic(自动的)。
Thorough(全面的)。
Repetable(可重复的)。
Independent(独立的)。
Professional(专业的)。

2.3.5 CORRECT边界条件

一致性(Conformance)——值是否符合预期的格式?
有序性(Ordering)——一组值是该有序的,还是无序的?
区间性(Range)——值是否在一个合理的最大值和最小值的范围之内?
引用、耦合性(Reference)——代码是否引用了一些不受代码本身直接控制的外部因素?
存在性(Existence)——值是否存在(例如,非null,非零,包含于某个集合等)
基数性(Cardinality)——是否恰好有足够的值?
时间性,绝对的或者相对的(Time)——所有事情是否都是按顺序发生的?是否在正确的时间?是否及时?


3 快速入门

本章的主要目标是在Spring+SpringMVC+MyBatis的基础架构上,从传统的Dao、Service、Controller,由下往上针对这三层完成一次完整的测试。通过这个例子,希望大家能够更加了解测试如何编写。

3.1 环境要求

本文采用JAVA 1.8.0_92Spring Framework 4.3.9.RELEASEJunit 4.12mysql-5.6.32通过测试,使用maven构建项目、idea作为编译器。

3.2 项目结构解析

项目结构

  • src/main/java/qingtian/example 程序的主要代码
  • src/main/resources
    • config 配置文件
    • mapper mybatis映射文件
    • sql 数据库脚本
    • logback.xml 日志配置文件
  • src/test 测试类

3.3 代码解析

BaseTest

src/test/下创建一个测试的基类,在这里设置了事务回滚,测试数据不会污染数据库。当然,并不是完全不对数据库造成影响。如果主键被设置为自动增长时,会发现ID

  • 40
    点赞
  • 241
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值