python的测试
测试
知识点
- 单元测试概念
- 使用 unittest 模块
- 测试用例的编写
- 异常测试
- 测试覆盖率概念
- 使用 coverage 模块
实验步骤
1. 应该测试什么?
如果可能的话,代码库中的所有代码都要测试。但这取决于开发者,如果写一个健壮性测试是不切实际的,你可以跳过它。就像 Nick Coghlan(Python 核心开发成员) 在访谈里面说的:有一个坚实可靠的测试套件,你可以做出大的改动,并确信外部可见行为保持不变。
2. 单元测试
这里引用维基百科的介绍:
在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
2.1. 单元测试模块
在 Python 里我们有 unittest 这个模块来帮助我们进行单元测试。
2.2. 阶乘计算程序
在这个例子中我们将写一个计算阶乘的程序 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
2.3. 测试哪个函数?
正如你所看到的, fact(n) 这个函数执行所有的计算,所以我们至少应该测试这个函数。
2.4. 第一个测试用例
编辑 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,然后看看会发生什么 :)
2.5. 各类 assert 语句
| Method | Checks that | 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 |
2.6. 异常测试
如果我们在 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()
2.7. 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
3. 测试覆盖率
测试覆盖率是找到代码库未经测试的部分的简单方法。它并不会告诉你的测试好不好。
在 Python 中我们已经有了一个不错的覆盖率工具来帮助我们。你可以在实验楼环境中安装它:
$ sudo pip3 install coverage
3.1. 覆盖率示例
$ 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 文件的形式输出覆盖率结果,然后在浏览器中查看它。
$ coverage3 html


总结
本实验了解了什么是单元测试,unittest 模块怎么用,测试用例怎么写。以及最后我们使用第三方模块 coverage 进行了覆盖率测试。
在实际生产环境中,测试环节是非常重要的的一环,即便志不在测试工程师,但以后的趋势就是 DevOps,所以掌握良好的测试技能也是很有用的。
python的测试的更多相关文章
- 老司机带你用vagrant打造一站式python开发测试环境
前言 作为一个学习和使用Python的老司机,好像应该经常总结一点东西的,让新司机尽快上路,少走弯路,然后大家一起愉快的玩耍. 今天,咱们就使用vagrant配合xshell打造一站式Python ...
- python nose测试框架全面介绍十---用例的跳过
又来写nose了,这次主要介绍nose中的用例跳过应用,之前也有介绍,见python nose测试框架全面介绍四,但介绍的不详细.下面详细解析下 nose自带的SkipTest 先看看nose自带的S ...
- Jenkins自动化构建python nose测试
[本文出自天外归云的博客园] 简介 通过Jenkins自动化构建python nose测试分两步: 1. 创建节点(节点就是执行自动化测试的机器): 2. 创建任务并绑定节点(用指定的机器来跑我们创建 ...
- python nose测试框架全面介绍七--日志相关
引: 之前使用nose框架时,一直使用--logging-config的log文件来生成日志,具体的log配置可见之前python nose测试框架全面介绍四. 但使用一段时间后,发出一个问题,生成的 ...
- python nose测试框架全面介绍六--框架函数别名
之前python nose测试框架全面介绍二中介绍了nose框架的基本构成,但在实际应该中我们也会到setup_function等一系列的名字,查看管网后,我们罗列下nose框架中函数的别名 1.pa ...
- Java or Python?测试开发工程师如何选择合适的编程语言?
很多测试开发工程师尤其是刚入行的同学对编程语言和技术栈选择问题特别关注,毕竟掌握一门编程语言要花不少时间成本,也直接关系到未来的面试和就业(不同企业/项目对技术栈要求也不一样),根据自身情况做一个相对 ...
- python doctest测试
title: Python doctest测试 tags: Python --- doctest测试 python 提供了REPL(read-eval-print loop,读取.求值.输出的循环) ...
- Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建;学习Python语言,利用Python语言来写测试用例。加油!!!
Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建:学习Python语言,利用Python语言来写测试用例.加油!!!
- Pytest权威教程-更改标准(Python)测试发现
目录 更改标准(Python)测试发现 在测试收集过程中忽略路径 测试期间收集的测试取消 保留从命令行指定的重复路径 更改目录递归 更改命名约定 将cmdline参数解释为Python包 找出收集的东 ...
- python环境测试MySQLdb、DBUtil、sqlobject性能
python环境测试MySQLdb.DBUtil.sqlobject性能 首先介绍下MySQLdb.DBUtil.sqlobject: (1)MySQLdb 是用于Python连接Mysql数据库的接 ...
随机推荐
- PyTorch官方中文文档:自动求导机制
自动求导机制 本说明将概述Autograd如何工作并记录操作.了解这些并不是绝对必要的,但我们建议您熟悉它,因为它将帮助您编写更高效,更简洁的程序,并可帮助您进行调试. 从后向中排除子图 每个变量都有 ...
- 【BZOJ2565】最长双回文串(回文树)
[BZOJ2565]最长双回文串(回文树) 题面 BZOJ 题解 枚举断点\(i\) 显然的,我们要求的就是以\(i\)结尾的最长回文后缀的长度 再加上以\(i+1\)开头的最长回文前缀的长度 至于最 ...
- [HNOI2013]消毒
题目大意: 网址:https://www.luogu.org/problemnew/show/3231 大意:a×b×c的三维空间里有a×b×c个点(x,y,z),其中有些点需要被消除. 消除的方法为 ...
- JavaScript:['1','2','3'].map(parseInt)问题解析
最近碰到了['1','2','3'].map(parseInt)这种看似不起眼陷阱却极大的问题. 这乍一看,感觉应该会输出[1,2,3].但是,实际上并不是我们想的这样.你可以现在打开console, ...
- php表单提交时获取不到post数据的解决方法
找到了一位博主的方法完美解决,链接如下: http://blog.csdn.net/whd526/article/details/53263181
- onmouse事件
常用的鼠标事件:onmouseenter,onmouseleave,onmouseover,onmouseout,onmouseup,onmousedown,onmousewheel,onmousem ...
- C++实验指导
选择 Build > Set Active Configuration 菜单,在对话框中选择 Win32 Debug. 当程序调试成功后,一般将其生成发布版,这样可使运行程序优化,方法是在上述对 ...
- 优先级队列用法详解(priority_queue)
由于优先级队列的内部数据结构为 堆,所以这里先介绍堆的一些操作. 堆的一些函数操作在algorithm头文件中 //在[first, last)范围内构造最大堆,first,last 可以是vecto ...
- Mycat 分片规则详解--ASCII 取模范围分片
实现方式:该算法与取模范围算法类似,该算法支持数值.符号.字母取模.首先截取长度为 prefixLength 的子串,在对子串中每一个字符的 ASCII 码求和,然后对求和值进行取模运算(sum%pa ...
- 笔记:XML-解析文档
要处理XML文档,就要先解析(parse)他,解析器时这样一个程序,读入一个文件,确认整个文件具有正确的格式,然后将其分解成各种元素,使得程序员能够访问这些元素,Java库提供了两种XML解析器: 像 ...