编写测试检验应用程序所有不同的功能。每一个测试集中在一个关注点上验证结果是不是期望的。定期执行测试确保应用程序按预期的工作。当测试覆盖很大的时候,通过运行测试你就有自信确保修改点和新增点不会影响应用程序。

测试范围

如果可能的话,代码库中的所有代码都要测试。但这取决于开发者,如果写一个健壮性测试是不切实际的,你可以跳过它。就像 Nick Coghlan(Python 核心开发成员) 在访谈里面说的:有一个坚实可靠的测试套件,你可以做出大的改动,并确信外部可见行为保持不变。

单元测试

这里引用维基百科的介绍:

在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

单元测试模块

在 Python 里我们有 unittest 这个模块来帮助我们进行单元测试。

阶乘计算程序

在这个例子中我们将写一个计算阶乘的程序 /home/shiyanlou/factorial.py

import sys

def fact(n):
"""
阶乘函数 :arg n: 数字
:returns: n 的阶乘 """
if n == 0:
return 1
return n * fact(n -1) def div(n):
"""
只是做除法
"""
res = 10 / n
return res def main(n):
res = fact(n)
print(res) if __name__ == '__main__':
if len(sys.argv) > 1:
main(int(sys.argv[1]))

运行程序:

$ python3 factorial.py 5

第一个测试用例

测试哪个函数?

正如你所看到的, fact(n) 这个函数执行所有的计算,所以我们至少应该测试这个函数。

编辑 /home/shiyanlou/factorial_test.py 文件,代码如下:

import unittest
from factorial import fact class TestFactorial(unittest.TestCase):
"""
我们的基本测试类
""" def test_fact(self):
"""
实际测试
任何以 `test_` 开头的方法都被视作测试用例
"""
res = fact(5)
self.assertEqual(res, 120) if __name__ == '__main__':
unittest.main()

运行测试:

$ python3 factorial_test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s OK

说明

我们首先导入了 unittest 模块,然后测试我们需要测试的函数。

测试用例是通过子类化 unittest.TestCase 创建的。

现在我们打开测试文件并且把 120 更改为 121,然后看看会发生什么?

各类assert函数

New in
assertEqual(a, b) a == b  
assertNotEqual(a, b) a != b  
assertTrue(x) bool(x) is True  
assertFalse(x) bool(x) is False  
assertIs(a, b) a is b 2.7
assertIsNot(a, b) a is not b 2.7
assertIsNone(x) x is None 2.7
assertIsNotNone(x) x is not None 2.7
assertIn(a, b) a in b 2.7
assertNotIn(a, b) a not in b 2.7
assertIsInstance(a, b) isinstance(a, b) 2.7
assertNotIsInstance(a, b) not isinstance(a, b) 2.7

异常检测

如果我们在 factorial.py 中调用 div(0),我们能看到异常被抛出。

我们也能测试这些异常,就像这样:

self.assertRaises(ZeroDivisionError, div, 0)

完整代码:

import unittest
from factorial import fact, div class TestFactorial(unittest.TestCase):
"""
我们的基本测试类
""" def test_fact(self):
"""
实际测试
任何以 `test_` 开头的方法都被视作测试用例
"""
res = fact(5)
self.assertEqual(res, 120) def test_error(self):
"""
测试由运行时错误引发的异常
"""
self.assertRaises(ZeroDivisionError, div, 0) if __name__ == '__main__':
unittest.main()

mounttab.py

mounttab.py 中只有一个 mount_details() 函数,函数分析并打印挂载详细信息。

import os

def mount_details():
"""
打印挂载详细信息
"""
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
print('{} on {} type {}'.format(words[0],words[1],words[2]), end=' ')
if len(words) > 5:
print('({})'.format(' '.join(words[3:-2])))
else:
print()
fd.close() if __name__ == '__main__':
mount_details()

重构 mounttab.py

现在我们在 mounttab2.py 中重构了上面的代码并且有一个我们能容易的测试的新函数 parse_mounts()

import os

def parse_mounts():
"""
分析 /proc/mounts 并 返回元祖的列表
"""
result = []
if os.path.exists('/proc/mounts'):
fd = open('/proc/mounts')
for line in fd:
line = line.strip()
words = line.split()
if len(words) > 5:
res = (words[0],words[1],words[2],'({})'.format(' '.join(words[3:-2])))
else:
res = (words[0],words[1],words[2])
result.append(res)
fd.close()
return result def mount_details():
"""
打印挂载详细信息
"""
result = parse_mounts()
for line in result:
if len(line) == 4:
print('{} on {} type {} {}'.format(*line))
else:
print('{} on {} type {}'.format(*line)) if __name__ == '__main__':
mount_details()

同样我们测试代码,编写 mounttest.py 文件:

#!/usr/bin/env python
import unittest
from mounttab2 import parse_mounts class TestMount(unittest.TestCase):
"""
我们的基本测试类
""" def test_parsemount(self):
"""
实际测试
任何以 `test_` 开头的方法都被视作测试用例
"""
result = parse_mounts()
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], tuple) def test_rootext4(self):
"""
测试找出根文件系统
"""
result = parse_mounts()
for line in result:
if line[1] == '/' and line[2] != 'rootfs':
self.assertEqual(line[2], 'ext4') if __name__ == '__main__':
unittest.main()

运行程序

$ python3 mounttest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s OK

测试覆盖率

测试覆盖率是找到代码库未经测试的部分的简单方法。它并不会告诉你的测试好不好。

在 Python 中我们已经有了一个不错的覆盖率工具来帮助我们。你可以在实验楼环境中安装它:

$ sudo pip3 install coverage

覆盖率示例

$ coverage3 run mounttest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.013s OK
$ coverage3 report -m
Name Stmts Miss Cover Missing
--------------------------------------------
mounttab2.py 22 7 68% 16, 25-30, 34
mounttest.py 14 0 100%
--------------------------------------------
TOTAL 36 7 81%

我们还可以使用下面的命令以 HTML 文件的形式输出覆盖率结果,然后在浏览器中查看它。

总结

本实验了解了什么是单元测试,unittest 模块怎么用,测试用例怎么写。以及最后我们使用第三方模块 coverage 进行了覆盖率测试。

在实际生产环境中,测试环节是非常重要的的一环,即便志不在测试工程师,但以后的趋势就是 DevOps,所以掌握良好的测试技能也是很有用的。

参考链接:https://www.shiyanlou.com/courses/596

Python3基础教程(十八)—— 测试的更多相关文章

  1. ComicEnhancerPro 系列教程十八:JPG文件长度与质量

    作者:马健邮箱:stronghorse_mj@hotmail.com 主页:http://www.comicer.com/stronghorse/ 发布:2017.07.23 教程十八:JPG文件长度 ...

  2. Python3基础(十二) 学习总结·附PDF

    Python是一门强大的解释型.面向对象的高级程序设计语言,它优雅.简单.可移植.易扩展,可用于桌面应用.系统编程.数据库编程.网络编程.web开发.图像处理.人工智能.数学应用.文本处理等等. 在学 ...

  3. Bootstrap <基础二十八>列表组

    列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 cl ...

  4. css3基础教程十六变形与动画animation

    前面我们讲过的变形与动画一般都是通过鼠标的单击.获得焦点,被点击或对元素进行一定改变后以后触发效果的,那么有没有像Flash一样自动播放的动画效果呢?答案当然是肯定的,这就是我们今天要讲到的anima ...

  5. SpringCloud2.0 Zuul 网关路由 基础教程(十)

    1.启动基础工程 1.1.启动[服务注册中心],工程名称:springcloud-eureka-server 参考 SpringCloud2.0 Eureka Server 服务中心 基础教程(二) ...

  6. Python3基础教程(二十)—— flask介绍

    基本概念 什么是Flask? Flask 是一个 web 框架.也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序.这个 web 应用程序可以是一些 web 页面.博客.w ...

  7. Python3基础教程(十九)—— 项目结构

    本节阐述了一个完整的 Python 项目结构,你可以使用什么样的目录布局以及怎样发布软件到网络上. 创建Python项目 我们的实验项目名为 factorial,放到 /home/shiyanlou/ ...

  8. Python3基础教程(十五)—— PEP8 代码风格指南

    编程语言不是艺术,而是工作或者说是工具,所以整理并遵循一套编码规范是十分必要的. 这篇文章原文实际上来自于这里:https://www.python.org/dev/peps/pep-0008/ 有很 ...

  9. Python3基础教程(十六)—— 迭代器、生成器、装饰器

    在这个实验里我们学习迭代器.生成器.装饰器有关知识. 这几个概念是 Python 中不容易理解透彻的概念,务必把所有的实验代码都完整的输入并理解清楚其中每一行的意思. 迭代器 Python 迭代器(I ...

随机推荐

  1. bzoj1025 [SCOI2009]游戏——因数DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1025 这篇博客写得真好呢:https://www.cnblogs.com/phile/p/4 ...

  2. Android适合组件化开发的路由框架:Launch

    1.概述 最近越来越不想写代码了,特别是一些重复性的代码,比如由于每次启动一个 Activity,我们都会很习惯的在 Activity 中写下: public static void launch(A ...

  3. mounted

    注意 mounted 不会承诺所有的子组件也都一起被挂载.如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted: mounted: function () { t ...

  4. Java递归应用:输出树形菜单

    转自:https://blog.csdn.net/zhangzeyuaaa/article/details/24574769

  5. mfc实现对话框全屏置顶显示

    一.MFC让对话框窗口始终在最前 方法一:在对话框的属性中,把SystemModal设置为True. 二.全屏显示 在CDialog7::OnInitDialog()中加入: 先取得分辨率, int ...

  6. hibernate的基础学习--多对多关联

    多对多采用学生老师模型进行测试 学生配置文件: <?xml version="1.0" encoding="utf-8" ?> <!DOCTY ...

  7. 基于puppeteer的网络拦截工具flyover

    为什么需要网络拦截工具 通常我们会遇到这样的场景: 线上一个图片有异常,线下修正之后需要发布之前进行测试,有些情况下,QA同学有直接在线上环境测试的需求,只是链接本地资源,通常在windows下有一个 ...

  8. E20180412-hm

    overflow  vt. 溢出,淹没; 挤满,充满; 洋溢; 资源过剩;   n. 泛滥,溢出物; 溢流管; 充溢,过多; 超出额;   vi. 泛滥,溢出; 充溢;

  9. Codeforces Round #421 (Div. 2)B. Mister B and Angle in Polygon(模拟+精度控制)

    传送门 题意 给出正n多边形和一个数a,寻找与a最接近的角,输出角编号 分析 找出多边形上所有角,一一比对即可 trick 1.判断的时候注意精度,i.e.x-eps>0 2.double与do ...

  10. locate的基本用法

    一.工作原理 1. locate是通过读取一个或多个由updatedb命令生成的数据库来查找文件的,而updatedb由计划任务程序cron每天运行来更新缺省的数据库(/var/lib/mlocate ...