前言

为什么需要单元测试?

如果没有单元测试,我们会遇到这种情况:已有的健康运行的代码在经过改动之后,我们无法得知改动之后是否引入了Bug。如果有单元测试的话,只要单元测试全部通过,我们就可以保证没有Bug被引入。因此,单元测试是保证软件工程质量的一个很重要的方面。

Python中的单元测试

Python最强大的地方在于,开发效率高,并且有丰富的Package,避免重复造轮子。那么Python中的Unittest模块有很丰富的功能提供给我们调用:完整的测试框架,丰富的拓展,比如我们可以设置测试之前的一些初始化工作,比如链接数据库等,规划测试集中有哪些测试用例需要跳过,以及跳过的原因。

Unittest中几个类(Class)的基本概念

TestCase 是我们要写的具体的测试用例
TestSuite 多个测试用例集合在一起,中文翻译过来叫测试套件,其实就是测试集。
TestLoader是用来加载TestCase到TestSuite中的(更通俗一点,就是用来把符合我们定义的条件的测试用例组合起来,成为一个测试集),一般会以参数的形式传进去一些条件,比如收集某个目录下所有的test case组成新的测试集。
TestRunner是来执行测试用例的,测试的结果会保存到TestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息

一个简单的测试例子

>>> class MyTest(unittest.TestCase):
#Run before whole testcase set execution, decorator classmethod is essential
@classmethod
def setUpClass(self):
print("UnitTest Begin...")
#Run after whole testcase set execution, decorator classmethod is essential
@classmethod
def tearDownClass(self):
print("UnitTest End...")
#Run before each test case execution
def setUp(self):
print("Begin...")
#Run after each test case execution
def tearDown(self):
print("End...")
def test_1(self):
self.assertEqual(1,1)
def test_2(self):
self.assertEqual(1,2) >>> if __name__ == '__main__':unittest.main() UnitTest Begin...
Begin...
End...
.Begin...
End...
FUnitTest End... ======================================================================
FAIL: test_2 (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<pyshell#41>", line 15, in test_2
AssertionError: 1 != 2 ----------------------------------------------------------------------
Ran 2 tests in 0.097s FAILED (failures=1)

在这个例子中,有几个函数要注意:

setUp()和tearDown():每个test case执行之前和执行之后要运行的操作:我们可以在这里定义测试的准备工作,比如链接数据库,web登录等等。

用装饰器classmethod装饰的setUpClass()和tearDownClass(): 跑类中定义的所有test cases之前和之后,需要执行的操作。

test_1()和test_2(),具体的测试用例,一定要以test为开头,因为unittest框架中,定义为,如果TestCase类中以test为开头的函数,将会作为具体的testcase收录进要执行的测试集里。

self.assertEqual(),是TestCase类中的断言函数,用来做判断的,用以判断该条测试用例是否通过。通过名字我们可以看出,这个函数的意思是判断两个值是否相等,如果相等,则用例通过,如果不等,则用例不通过。类似的,断言函数还有很多:有一个msg参数,如果指定msg参数的值,则将该信息作为失败的错误信息返回

 

三种常见测试写法

第一种: 搜索该模块下所有以test开头的测试用例方法,并自动执行它们

#执行测试用例方案一如下:
#unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行它们。 import unittest #定义测试类,父类为unittest.TestCase。
#可继承unittest.TestCase的方法,如setUp和tearDown方法,不过此方法可以在子类重写,覆盖父类方法。
#可继承unittest.TestCase的各种断言方法。
class Test(unittest.TestCase): def setUp(self):
self.number=raw_input('Enter a number:')
self.number=int(self.number) #定义测试用例,以“test_”开头命名的方法
#可使用unittest.TestCase类下面的各种断言方法用于对测试结果的判断
def test_case1(self):
print self.number
self.assertEqual(self.number,10,msg='Your input is not 10') def test_case2(self):
print self.number
self.assertEqual(self.number,20,msg='Your input is not 20') @unittest.skip('暂时跳过用例3的测试')
def test_case3(self):
print self.number
self.assertEqual(self.number,30,msg='Your input is not 30') def tearDown(self):
print 'Test over' #如果直接运行该文件(__name__值为__main__),则执行以下语句,常用于测试脚本是否能够正常运行
if __name__=='__main__':
#执行顺序是命名顺序:先执行test_case1,再执行test_case2
unittest.main()

第二种: 构造测试集,实例化test suite(即TestRunner类), 运行test suite中所有的测试用例。

'''
执行测试用例方案二如下:
先构造测试集
实例化测试套件
'''
suite=unittest.TestSuite()
#将测试用例加载到测试套件中。
#执行顺序是安装加载顺序:先执行test_case2,再执行test_case1
suite.addTest(Test('test_case2'))
suite.addTest(Test('test_case1'))
#执行测试用例
#实例化TextTestRunner类
runner=unittest.TextTestRunner()
#使用run()方法运行测试套件(即运行测试套件中的所有用例)
runner.run(suite)

第三种:通过收集指定目录下的目标测试用例,构造测试集再执行

#构造测试集(简化了方案二中先要创建测试套件然后再依次加载测试用例)
#执行顺序同方案一:执行顺序是命名顺序:先执行test_case1,再执行test_case2
test_dir = './'
discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
#执行测试用例
#实例化TextTestRunner类
runner=unittest.TextTestRunner()
#使用run()方法运行测试套件(即运行测试套件中的所有用例)
runner.run(discover)

如何生成HTML和XML格式的测试报告

上面我们得到的测试结果是文本格式的,可读性不好,并且也无法直接用来作后续的测试结果数据处理,比如测试结果的分类统计等等。

那么有两种方式可供我们选择:HTML和XML

HTML格式的报告,就是网页格式,可读性会比较好。XML格式的报告,则比较方便作后续的数据处理。

需要注意的是,我们需要安装额外的package,即HtmlTestRunner和xmlrunner。

在配置好Pip的前提下,可以通过以下命令安装:

pip install html-testrunner

pip instll xmlrunner

如果没有配置好pip或者用pip安装失败,则需要用以下方式安装(xmlrunner同理):

1. 下载HTMLTestRunner.py文件:地址http://tungwaiyip.info/software/HTMLTestRunner.html
2. 将该文件保存在python安装路径下的lib文件夹中。在文件中能import HTMLTestRunner成功,即配置成功。
 
以生成HTML报告为例:
#!/usr/bin/python3
# -*- coding: utf-8 -*- import unittest
import HtmlTestRunner class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(),'FOO')
def test_isupper(self):
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(),['hello','world'])
with self.assertRaises(TypeError):
s.split(2) if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(TestStringMethods('test_upper'))
suite.addTest(TestStringMethods('test_isupper'))
suite.addTest(TestStringMethods('test_split'))
runner = HtmlTestRunner.HTMLTestRunner(output='MyUnitTest')
runner.run(suite)

最终我们会得到一个可读性比较好的网页报告。

XML报告:

有时我们需要得到格式化数据的测试报告,此时XML格式就要比HTML格式好的多。

因为XML格式的test result容易被读取和数据处理。

示例代码如下:

#!/usr/bin/python3
# -*- coding: utf-8 -*- import unittest
import xmlrunner class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(),'FOO')
def test_isupper(self):
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(),['hello','world'])
with self.assertRaises(TypeError):
s.split(2) if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(TestStringMethods('test_upper'))
suite.addTest(TestStringMethods('test_isupper'))
suite.addTest(TestStringMethods('test_split'))
#fp = open('result.html','w')
runner = xmlrunner.XMLTestRunner(output='MyUnitTest')
#runner = HtmlTestRunner.HTMLTestRunner(stream=fp,output='MyUnitTest')
runner.run(suite)

得到的结果是这样的:

<?xml version="1.0"?>

-<testsuite time="0.000" tests="3" name="TestStringMethods-20181115000346" failures="0" errors="0">

<testcase time="0.000" name="test_upper" classname="TestStringMethods"/>

<testcase time="0.000" name="test_isupper" classname="TestStringMethods"/>

<testcase time="0.000" name="test_split" classname="TestStringMethods"/>

-<system-out>

<![CDATA[]]>

</system-out>

-<system-err>

<![CDATA[]]>

</system-err>

</testsuite>

几个利用unittest做测试的实际例子

百度搜索测试用例

from selenium import webdriver
import unittest, time class BaiduTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30) #隐性等待时间为30秒
self.base_url = "https://www.baidu.com" def test_baidu(self):
driver = self.driver
driver.get(self.base_url + "/")
driver.find_element_by_id("kw").clear()
driver.find_element_by_id("kw").send_keys("unittest")
driver.find_element_by_id("su").click()
time.sleep(3)
title=driver.title
self.assertEqual(title, u"unittest_百度搜索") def tearDown(self):
self.driver.quit() if __name__ == "__main__":
unittest.main()

有道翻译测试用例

from selenium import webdriver
import unittest, time class YoudaoTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30) #隐性等待时间为30秒
self.base_url = "http://www.youdao.com" def test_youdao(self):
driver = self.driver
driver.get(self.base_url + "/")
driver.find_element_by_id("translateContent").clear()
driver.find_element_by_id("translateContent").send_keys(u"你好")
driver.find_element_by_id("translateContent").submit()
time.sleep(3)
page_source=driver.page_source
self.assertIn( "hello",page_source) def tearDown(self):
self.driver.quit() if __name__ == "__main__":
unittest.main()

web测试用例:通过测试套件TestSuite来组装多个测试用例。

import unittest
from test_case import test_baidu
from test_case import test_youdao #构造测试集
suite = unittest.TestSuite()
suite.addTest(test_baidu.BaiduTest('test_baidu'))
suite.addTest(test_youdao.YoudaoTest('test_youdao')) if __name__=='__main__':
#执行测试
runner = unittest.TextTestRunner()
runner.run(suite)

参考链接:

1. unittest单元测试框架总结

2. unittest — Unit testing framework https://docs.python.org/3/library/unittest.html

3. Python单元测试unittest

4. Python3 unittest断言详解

5. Python3 unittest单元测试

6. Python HTMLTestRunner 学习

Python中的单元测试模块Unittest快速入门的更多相关文章

  1. python中的单元测试模块unittest

    unittest的属性: 该文以思维导图的形式描述unittest的重要属性. 其中前四个是unittest最核心的三个属性. testcase:测试用例: testsuite:测试套件,多个测试用例 ...

  2. Python中定时任务框架APScheduler的快速入门指南

    前言 大家应该都知道在编程语言中,定时任务是常用的一种调度形式,在Python中也涌现了非常多的调度模块,本文将简要介绍APScheduler的基本使用方法. 一.APScheduler介绍 APSc ...

  3. [ Python入门教程 ] Python中日志记录模块logging使用实例

    python中的logging模块用于记录日志.用户可以根据程序实现需要自定义日志输出位置.日志级别以及日志格式. 将日志内容输出到屏幕 一个最简单的logging模块使用样例,直接打印显示日志内容到 ...

  4. Python入门之Python中的logging模块

    基本用法 下面的代码展示了logging最基本的用法. import logging import sys # 获取logger实例,如果参数为空则返回root logger logger = log ...

  5. python中的单元测试pyUnit

    python中的单元测试pyUnit   在Python中进行单元测试时需要用到PyUnit模块,Python 2.1及其以后的版本都将PyUnit作为一个标准模块,但如果你使用的是较老版本的Pyth ...

  6. Python中的random模块,来自于Capricorn的实验室

    Python中的random模块用于生成随机数.下面介绍一下random模块中最常用的几个函数. random.random random.random()用于生成一个0到1的随机符点数: 0 < ...

  7. Python中的logging模块

    http://python.jobbole.com/86887/ 最近修改了项目里的logging相关功能,用到了python标准库里的logging模块,在此做一些记录.主要是从官方文档和stack ...

  8. Python中的random模块

    Python中的random模块用于生成随机数.下面介绍一下random模块中最常用的几个函数. random.random random.random()用于生成一个0到1的随机符点数: 0 < ...

  9. 浅析Python中的struct模块

    最近在学习python网络编程这一块,在写简单的socket通信代码时,遇到了struct这个模块的使用,当时不太清楚这到底有和作用,后来查阅了相关资料大概了解了,在这里做一下简单的总结. 了解c语言 ...

随机推荐

  1. 自学youku_web

    仿youku架构 数据库设计 管理员 注册 登录 上传视频 删除视频 发布公告 普通用户 注册 登录 充会员 查看视频 下载免费视频 下载收费视频 查看观影记录 查看公告 思路 class Field ...

  2. java 集合 HashSet 实现随机双色球 HashSet addAll() 实现去重后合并 HashSet对象去重 复写 HashCode()方法和equals方法 ArrayList去重

    package com.swift.lianxi; import java.util.HashSet; import java.util.Random; /*训练知识点:HashSet 训练描述 双色 ...

  3. 使用免费公开的api接口示例(iOS)

    做项目难免需要测试,要测试就需要一些接口,现在网上的很多接口都是需要收费的. 以下是目前找到的免费 JSON API免费接口 云聚数据 网吧数据 其中选取了一个百度百科的接口 百度接口 百度百科接口: ...

  4. 爬虫学习(十九)——Scrapy的学习及其使用

    Scrapy框架的介绍 Scrapy,非常的强悍,通过python语言编写的,非常知名的爬虫框架 框架工作流程 框架流程图 基本工作流程; 1.引擎向spiders要url 2.引擎将要爬取的url给 ...

  5. 【PGP公钥】

    Fingerprint: 37AF 3814 3ABC 5DFA 97F5 300E 581D A2E3 F4D2 F585 Key ID:0x581DA2E3F4D2F585 -----BEGIN ...

  6. python——内建模块instance的学习

    python中内建函数isinstance的用法 语法:isinstance(object,type) 作用:来判断一个对象是否是一个已知的类型. 其第一个参数(object)为对象,第二个参数(ty ...

  7. 001---web应用程序

    什么是web应用? 应用程序分两种模式:C/S.B/S 1 .C/S:客户端(Client)与服务端 一般独立运行 2 .B/S:浏览器(Browser)与服务端 这类应用要借助浏览器:谷歌.火狐.I ...

  8. TouTiao开源项目 分析笔记10 实现通用普通文章片段页面

    1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...

  9. 使用 Ajax

    Ajax( Asynchronous JavaScript and XML) 在 Ajax 中 Asynchronous 是指异步, 代表 客户端(Client 通常是指浏览器) 可以向服务器(Ser ...

  10. python os模块练习题

    # 1.获取某个文件所在目录的上一级目录. # 例如'D:\python\projects\test19.py'目录的结果 :D:\python\projects # 方法1 # path = os. ...