单元测试

据统计,由于软件缺陷(bug),美国经济每年在浪费生产力、返工和实际毁坏上损失了数十亿美元。近期最严重的案例是波音737 Max飞机的两次重大坠机事故,共造成了346人死亡。经过初步调查,该公司的专用软件难辞其咎。因此,通过软件工程方法以及测试减少软件的缺陷,十分重要。

版权声明

本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。

本文不可以以纸质出版为目的进行改编、摘抄。

本文节选自作者的《Python编程基础及应用》视频教程。

代码错误或者软件bug和程序员如影随形。作为程序员,我们经常在担心:“我编写的代码是否正确?我编写的代码有没有bug?”。测试驱动开发提供的测试集可以减轻你的担心。

1. 测试驱动开发

测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它的基本思路就是通过测试来推动整个开发的进行。在明确要开发某个功能后,不是直接实现功能部分,而是先思考如何对这个功能进行测试,先完成测试代码的编写,然后再编写相关的功能代码满足这些测试用例。

测试驱动开发的基本过程如下:a. 先明确要实现的功能并进行分析;b. 为该功能的测试设计用例并编写测试代码,并在测试用例设计过程中检视该功能的接口;c. 编写代码实现功能;d. 使用测试用例进行测试;e. 如果测试没有通过,对代码进行重构,直至测试通过为止;f. 使用相同流程循环完成软件其它功能的开发。

读者也可能听说过一种叫做“极限编程”的轻量级软件工程思想。在“极限编程”里,一个重要的规则就是:先写测试,这跟TDD异曲同工。测试驱动开发的一个重要实施手段就是单元测试。

2. 单元测试

单元测试是保证软件模块质量的重要手段之一,通过单元测试来管理设计好的测试用例,不仅可以避免测试过程中的人工输入引起的错误,还可以重复利用设计好的测试用例。Python标准库unittest提供了很多用于单元测试的类和方法,其中最常用的是TestCase类,其常用方法如表所示。

名称 功能 名称 功能
assertEqual(a, b) a == b assertIsNone(x) x is None
assertNotEqual(a, b) a != b assertIsNotNone(x) x is not None
assertTrue(x) bool(x) is True assertIn(a, b) a in b
assertFalse(x) bool(x) is False assertNotIn(a, b) a not in b
assertIs(a, b) a is b assertIsInstance(a, b) isinstance(a, b)
assertIsNot(a, b) a is not b assertNotIsInstance(a, b) not isinstance(a, b)
assertAlmostEqual(a, b) round(a-b, 7) == 0 assertNotAlmostEqual(a, b) round(a-b, 7) != 0
assertGreater(a, b) a > b assertGreaterEqual(a, b) a >= b
assertLess(a, b) a < b assertLessEqual(a, b) a <= b
assertRegex(s, r) r.search(s) assertNotRegex(s, r) not r.search(s)

基于测试驱动开发的思想,我们编写一个类来测试判断素数的函数。首先,我们定义了该素数判断函数的接口:函数名为isPrime,接受一个整数参数num, 如果num是素数,该函数返回True, 否则返回False。该函数的接口可以使用伪代码表示如下。

bool isPrime(int num)

根据TDD, 先写测试,测试类如下:

#testisprime.py
from prime import isPrime
import unittest
class IsPrimeTestCase(unittest.TestCase):def testIsPrime(self):self.assertEqual(isPrime(2),True,'素数判断错误')self.assertEqual(isPrime(7),True,'素数判断错误')self.assertEqual(isPrime(12),False,'12不是素数,判断错误')self.assertEqual(isPrime(0),False,'0不是素数,判断错误')self.assertEqual(isPrime(1),False,'1不是素数,判断错误')self.assertEqual(isPrime(-7),False,'负数不是素数')if __name__ == '__main__':unittest.main()

我们看到,测试类中把2,7,12以及比较特殊的0,1等作为参数,交给isPrime(num)函数进行判断,并把返回值与预期的值进行比较。如果返回值与预期值不一致,则会导致断言失败,这个失败的断言还会附带一个说明字符串帮助测试者确定测试未通过的原因。

接下来,根据需求以及在测试用例设计过程中检视过的功能接口实现isPrime()函数,版本为0.1:

#prime.py  v0.1
def isPrime(num):for i in range(2,num):if num % i == 0:return  Falsereturn True

接下来,运行testisprime.py进行测试,得到如下错误结果:

======================================================================
FAIL: testIsPrime (__main__.IsPrimeTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):File "d:\pylearn\C12_UnitTestException\testisprime.py", line 9, in testIsPrimeself.assertEqual(isPrime(0),False,'0不是素数,判断错误')
AssertionError: True != False : 0不是素数,判断错误----------------------------------------------------------------------
Ran 1 test in 0.001sFAILED (failures=1)

该错误结果表明,测试没有通过,这说明0.1版本的功能代码存在缺陷。仔细查看,发现对值0的素数判断结果真,而期望值应为假。重新检视isPrime(num)函数的设计,发现该函数遗漏了对值0和1的处理,进行修改,得到prime.py的0.2版本:

#prime.py  v0.2
def isPrime(num):if num in (0,1):return  False    for i in range(2,num):if num % i == 0:return  Falsereturn True

再次运行testisprime.py进行测试,结果如下:

======================================================================
FAIL: testIsPrime (__main__.IsPrimeTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):File "d:\pylearn\C12_UnitTestException\testisprime.py", line 11, in testIsPrimeself.assertEqual(isPrime(-7),False,'负数不是素数')
AssertionError: True != False : 负数不是素数----------------------------------------------------------------------
Ran 1 test in 0.001sFAILED (failures=1)

又没有通过测试,这次是因为num为负数的情况没有得到处理。审视并修改prime.py,得到0.3版本:

#prime.py  v0.3
def isPrime(num):if num < 0 or num in (0,1):return  False    for i in range(2,num):if num % i == 0:return  Falsereturn True

再次运行测试,结果如下:

----------------------------------------------------------------------
Ran 1 test in 0.000sOK

这次通过了,如果我们的测试用例设计得充分而又合理,可以认为isPrime(num)函数“合格”了。prime.py的版本号可以从0.3直升至1.0了。

上面这个极其微小的例子是希望浅显地向读者介绍单元测试的基本过程,真实的情况要复杂得多。几乎很少有程序员可以一次性地写出没有缺陷或者极少缺陷的代码,程序总是在不断修正,重构中提升其质量。除了写代码外,如何设计合理有效的测试用例,也是一项专门的学问,它属于软件工程的范畴。读者大致可以看到,对于isPrime(num)这样一个简单的函数,测试用例测试了边界值(0,1),正数,负数等各种情况,试图覆盖isPrime(num)函数全部可能的执行路径。

本文节选自作者的《Python编程基础及应用》视频教程及同名教科书。想完整零基础学习Python程序设计,欢迎使用此免费视频教程。

见微智著 - 从一个小例子看Python中的单元测试相关推荐

  1. 从一个小例子看贝叶斯公式的应用(学习简单、基础、入门的例子)

    #从一个小例子看贝叶斯公式的应用 ###应用Bayesian公式考察如下的实例并回答问题. 张某为了解自己患上了X疾病的可能性,去医院作常规血液检查.其结果居然为阳性,他赶忙到网上查询.根据网上的资料 ...

  2. 洗碗工问题 栈的一个小例子

    洗碗工 Python栈的一个小例子 前言 Python栈的一个小例子 在学习python的数据结构与算法时,在栈这一章节有一道洗碗工的问题,很有意思,有一定的难度,在csdn上找到的好几个代码中很多都 ...

  3. 飞鸽传书举一个小例子

    飞鸽传书举一个小例子,每个程序窗口右上角都有三个按钮,如最小化.关闭等.乔布斯觉得不好看,不易用,他提出改用红绿蓝的交通灯.这样修改后,果然 Mac OS 漂亮和易用了很多. 目前互联网和手机上的应用 ...

  4. MVVM模式的一个小例子

    使用SilverLight.WPF也有很长时间了,但是知道Binding.Command的基本用法,对于原理性的东西,一直没有深究.如果让我自己建一个MVVM模式的项目,感觉还是无从下手,最近写了一个 ...

  5. C# 实现 rtc_通过Xlua实现unity热更新的一个小例子

    通过Xlua实现unity热更新的一个小例子 一.介绍 ​ 热更新是指在更新游戏资源或者逻辑的时候不需要开发者将游戏再打包.发布.玩家重新下载安装包,而是可以通过将需要更新的资源打包成AssetBun ...

  6. puppet运行慢的一个小例子

    一个小例子来看下怎么debug puppet运行慢的问题. 一个小例子来看下怎么debug puppet运行慢的问题. 发现一台机器的agent运行比较缓慢,首先看下puppet server的整体性 ...

  7. unity案例 mysql lua_通过Xlua实现unity热更新的一个小例子

    通过Xlua实现unity热更新的一个小例子 一.介绍 ​ 热更新是指在更新游戏资源或者逻辑的时候不需要开发者将游戏再打包.发布.玩家重新下载安装包,而是可以通过将需要更新的资源打包成AssetBun ...

  8. html5 websocket插件,html5实现的WebSocket一个小例子(附代码)

    本篇文章给大家带来的内容是关于html5实现的WebSocket一个小例子(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 客户端代码: var socket; if (&q ...

  9. 网站制作---asp操作数据库的一个小例子

    qq交流群:创梦技术交流群:251572072  教程下载,在线交流:创梦IT社区:www.credream.com 一般用在asp网站中比较常见: asp操作数据库的一个小例子: id=reques ...

最新文章

  1. 重载赋值运算符含动态申请
  2. ***F漏洞分析与利用
  3. 【渝粤教育】国家开放大学2018年秋季 0161-22T教师职业道德 参考试题
  4. paip.字符串操作uapi java php python总结..
  5. Java 如何查询硬盘所有文件
  6. erp服务器维护数据备份,erp服务器备份
  7. php面试题 mysql 主从_php面试题之五——MySQL数据库(基础部分)
  8. 手机12306买卧铺下铺技巧_购买火车票下铺实用技巧
  9. MessageBox 用法
  10. [填坑]QT信号与槽机制注意事项
  11. PAT a1125
  12. 关于win8的各种版本的区别
  13. Python描述 LeetCode 781. 森林中的兔子
  14. 浅谈计算机领域及职业憧憬
  15. 初学前端需要掌握的HTML知识点
  16. 解决了一次模块间TTL串口不能通讯的问题
  17. Unity开发日记【第七天】——怪物的移动和动画及类的实现
  18. Linux下的CreateEvent WaitForSingleObject
  19. MSP432库函数学习笔记-CS
  20. SpringCloud(H版以及Alibaba版本)的学习笔记(三)

热门文章

  1. 常见的图像重采样算法
  2. 关于驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server的解决方案
  3. CentOS 官网镜像最新下载地址
  4. 快手小说怎么引流?门槛太低,是个人就能做
  5. 案例19:Java私房菜定制上门服务系统设计与实现开题报告
  6. 什么是高铁运营监测(转载)
  7. 【软件】网梭浏览器v2.4.7 思路
  8. 一文读懂智能对话系统
  9. 如何升级Windows 11正式版?四种方案都在这里了
  10. info setup 制作的安装程序 提示卸载程序出现内部错误找不到此版本的卸载程序的utCompiledCode记录