测试

知识点

  • 单元测试概念
  • 使用 unittest 模块
  • 测试用例的编写
  • 异常测试
  • 测试覆盖率概念
  • 使用 coverage 模块

实验步骤

1. 应该测试什么?

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

2. 单元测试

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

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

2.1. 单元测试模块

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

2.2. 阶乘计算程序

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

  1. import sys
  2. def fact(n):
  3. """
  4. 阶乘函数
  5. :arg n: 数字
  6. :returns: n 的阶乘
  7. """
  8. if n == 0:
  9. return 1
  10. return n * fact(n -1)
  11. def div(n):
  12. """
  13. 只是做除法
  14. """
  15. res = 10 / n
  16. return res
  17. def main(n):
  18. res = fact(n)
  19. print(res)
  20. if __name__ == '__main__':
  21. if len(sys.argv) > 1:
  22. main(int(sys.argv[1]))

运行程序:

  1. $ python3 factorial.py 5

2.3. 测试哪个函数?

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

2.4. 第一个测试用例

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

  1. import unittest
  2. from factorial import fact
  3. class TestFactorial(unittest.TestCase):
  4. """
  5. 我们的基本测试类
  6. """
  7. def test_fact(self):
  8. """
  9. 实际测试
  10. 任何以 `test_` 开头的方法都被视作测试用例
  11. """
  12. res = fact(5)
  13. self.assertEqual(res, 120)
  14. if __name__ == '__main__':
  15. unittest.main()

运行测试:

  1. $ python3 factorial_test.py
  2. .
  3. ----------------------------------------------------------------------
  4. Ran 1 test in 0.000s
  5. 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),我们能看到异常被抛出。

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

  1. self.assertRaises(ZeroDivisionError, div, 0)

完整代码:

  1. import unittest
  2. from factorial import fact, div
  3. class TestFactorial(unittest.TestCase):
  4. """
  5. 我们的基本测试类
  6. """
  7. def test_fact(self):
  8. """
  9. 实际测试
  10. 任何以 `test_` 开头的方法都被视作测试用例
  11. """
  12. res = fact(5)
  13. self.assertEqual(res, 120)
  14. def test_error(self):
  15. """
  16. 测试由运行时错误引发的异常
  17. """
  18. self.assertRaises(ZeroDivisionError, div, 0)
  19. if __name__ == '__main__':
  20. unittest.main()

2.7. mounttab.py

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

  1. import os
  2. def mount_details():
  3. """
  4. 打印挂载详细信息
  5. """
  6. if os.path.exists('/proc/mounts'):
  7. fd = open('/proc/mounts')
  8. for line in fd:
  9. line = line.strip()
  10. words = line.split()
  11. print('{} on {} type {}'.format(words[0],words[1],words[2]), end=' ')
  12. if len(words) > 5:
  13. print('({})'.format(' '.join(words[3:-2])))
  14. else:
  15. print()
  16. fd.close()
  17. if __name__ == '__main__':
  18. mount_details()

重构 mounttab.py

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

  1. import os
  2. def parse_mounts():
  3. """
  4. 分析 /proc/mounts 并 返回元祖的列表
  5. """
  6. result = []
  7. if os.path.exists('/proc/mounts'):
  8. fd = open('/proc/mounts')
  9. for line in fd:
  10. line = line.strip()
  11. words = line.split()
  12. if len(words) > 5:
  13. res = (words[0],words[1],words[2],'({})'.format(' '.join(words[3:-2])))
  14. else:
  15. res = (words[0],words[1],words[2])
  16. result.append(res)
  17. fd.close()
  18. return result
  19. def mount_details():
  20. """
  21. 打印挂载详细信息
  22. """
  23. result = parse_mounts()
  24. for line in result:
  25. if len(line) == 4:
  26. print('{} on {} type {} {}'.format(*line))
  27. else:
  28. print('{} on {} type {}'.format(*line))
  29. if __name__ == '__main__':
  30. mount_details()

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

  1. #!/usr/bin/env python
  2. import unittest
  3. from mounttab2 import parse_mounts
  4. class TestMount(unittest.TestCase):
  5. """
  6. 我们的基本测试类
  7. """
  8. def test_parsemount(self):
  9. """
  10. 实际测试
  11. 任何以 `test_` 开头的方法都被视作测试用例
  12. """
  13. result = parse_mounts()
  14. self.assertIsInstance(result, list)
  15. self.assertIsInstance(result[0], tuple)
  16. def test_rootext4(self):
  17. """
  18. 测试找出根文件系统
  19. """
  20. result = parse_mounts()
  21. for line in result:
  22. if line[1] == '/' and line[2] != 'rootfs':
  23. self.assertEqual(line[2], 'ext4')
  24. if __name__ == '__main__':
  25. unittest.main()

运行程序

  1. $ python3 mounttest.py
  2. ..
  3. ----------------------------------------------------------------------
  4. Ran 2 tests in 0.001s
  5. OK

3. 测试覆盖率

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

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

  1. $ sudo pip3 install coverage

3.1. 覆盖率示例

  1. $ coverage3 run mounttest.py
  2. ..
  3. ----------------------------------------------------------------------
  4. Ran 2 tests in 0.013s
  5. OK
  6. $ coverage3 report -m
  7. Name Stmts Miss Cover Missing
  8. --------------------------------------------
  9. mounttab2.py 22 7 68% 16, 25-30, 34
  10. mounttest.py 14 0 100%
  11. --------------------------------------------
  12. TOTAL 36 7 81%

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

  1. $ coverage3 html

总结

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

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

python的测试的更多相关文章

  1. 老司机带你用vagrant打造一站式python开发测试环境

      前言 作为一个学习和使用Python的老司机,好像应该经常总结一点东西的,让新司机尽快上路,少走弯路,然后大家一起愉快的玩耍. 今天,咱们就使用vagrant配合xshell打造一站式Python ...

  2. python nose测试框架全面介绍十---用例的跳过

    又来写nose了,这次主要介绍nose中的用例跳过应用,之前也有介绍,见python nose测试框架全面介绍四,但介绍的不详细.下面详细解析下 nose自带的SkipTest 先看看nose自带的S ...

  3. Jenkins自动化构建python nose测试

    [本文出自天外归云的博客园] 简介 通过Jenkins自动化构建python nose测试分两步: 1. 创建节点(节点就是执行自动化测试的机器): 2. 创建任务并绑定节点(用指定的机器来跑我们创建 ...

  4. python nose测试框架全面介绍七--日志相关

    引: 之前使用nose框架时,一直使用--logging-config的log文件来生成日志,具体的log配置可见之前python nose测试框架全面介绍四. 但使用一段时间后,发出一个问题,生成的 ...

  5. python nose测试框架全面介绍六--框架函数别名

    之前python nose测试框架全面介绍二中介绍了nose框架的基本构成,但在实际应该中我们也会到setup_function等一系列的名字,查看管网后,我们罗列下nose框架中函数的别名 1.pa ...

  6. Java or Python?测试开发工程师如何选择合适的编程语言?

    很多测试开发工程师尤其是刚入行的同学对编程语言和技术栈选择问题特别关注,毕竟掌握一门编程语言要花不少时间成本,也直接关系到未来的面试和就业(不同企业/项目对技术栈要求也不一样),根据自身情况做一个相对 ...

  7. python doctest测试

    title: Python doctest测试 tags: Python --- doctest测试 python 提供了REPL(read-eval-print loop,读取.求值.输出的循环) ...

  8. Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建;学习Python语言,利用Python语言来写测试用例。加油!!!

    Python+selenium测试环境成功搭建,简单控制浏览器(firefox)接下来,继续学习其他浏览器上的测试环境搭建:学习Python语言,利用Python语言来写测试用例.加油!!!

  9. Pytest权威教程-更改标准(Python)测试发现

    目录 更改标准(Python)测试发现 在测试收集过程中忽略路径 测试期间收集的测试取消 保留从命令行指定的重复路径 更改目录递归 更改命名约定 将cmdline参数解释为Python包 找出收集的东 ...

  10. python环境测试MySQLdb、DBUtil、sqlobject性能

    python环境测试MySQLdb.DBUtil.sqlobject性能 首先介绍下MySQLdb.DBUtil.sqlobject: (1)MySQLdb 是用于Python连接Mysql数据库的接 ...

随机推荐

  1. Jenkins + Github持续集成构建Docker容器,维基百科&人工自能(AI)模块

    本文分两部分,第一部分是手动计划任务的方式构建Github上的Docker程序,第二部分是用Github webhook Trigger一个自动构建任务. Jenkins采用2.5版本Docker采用 ...

  2. 【BZOJ3626】LCA(树链剖分,Link-Cut Tree)

    [BZOJ3626]LCA(树链剖分,Link-Cut Tree) 题面 Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. ...

  3. Luogu[POI2005]KOS-Dicing

    题面 二分后用网络流判定 S->人,流量为二分的mid 人->比赛,流量为1 比赛->T,流量为1 输出方案只要判断a就可以了 # include <bits/stdc++.h ...

  4. Bzoj1901 Dynamic Ranking

    动态区间第k小 离散化后 那么每个点开一棵线段树(主席树)再套一个树状数组在外面 每次询问区间内的树的个数时 相当于进行了一次树状数组求区间和的操作,只是是把树状数组那个点看做主席树,对log棵主席树 ...

  5. [转][RabbitMQ+Python入门经典] 兔子和兔子窝

    [转][RabbitMQ+Python入门经典] 兔子和兔子窝 http://blog.csdn.net/linvo/article/details/5750987 RabbitMQ作为一个工业级的消 ...

  6. MapReduce浅析

    很早之前就用过Hadoop,但对MapReduce中的具体数据流向过程一直不甚明了,用Python Streamming的方式写了几个MapReduce,对这个过程有了一定的认识. 首先我们知道,Ma ...

  7. linux下关闭网络命令

    CTRL+ALT+F1 进入命令行模式 CTRL+ALT+F7 退出命令行模式 sudo ifconfig ethX dwon 关闭网卡sudo /etc/init.d/networking stop ...

  8. IPFS:世界正在悄然发生变化

    世界正在悄然发生变化(IPFS) 2015-05-05 Juan Benet 在自己的终端里面敲入了下面的文字: > echo "hello worlds" | ipfs a ...

  9. Android工程化开发这门学科的看法

    http://www.cnblogs.com/unruledboy/p/DevCareer.html http://coolshell.cn/articles/4561.html http://blo ...

  10. Java 英文面试题

    1. Q: What is HashMap and Map?A: Map is Interface and Hashmap is class that implements that. 2. Q: D ...