Python  unittest单元测试框架的使用


Posted in Python onSeptember 08, 2018

一、测试模型

下面这部分来自于某书籍资料,拿过来,按需参考一下:

测试模型

(1)线性测试

1、概念:

通过录制或编写对应应用程序的操作步骤产生的线性脚本。单纯的来模拟用户完整的操作场景。(操作,重复操作,数据)都混合在一起。

2、优点:每个脚本相对独立,且不产生其他依赖和调用。任何一个测试用例脚本拿出来都可以单独执行。

3、缺点:开发成本高,用例之间存在重复的操作。比如重复的用户登录和退出。

维护成本高,由于重复的操作,当重复的操作发生改变时,则需要逐一进行脚本的修改。

4.线性测试实例:用户登录

(2)模块化驱动测试

1、概念:

将重复的操作独立成功共模块,当用例执行过程中需要用到这一模块操作时则被调用。
操作+(重复操作,数据)混合在一起。例如,自动化测试的执行需要保持测试用例的独立性和完整性,所以每一条用例在执行时都需要登录和退出操作,so可以把登录和退出的操作封装为公共函数。

2、优点:由于最大限度消除了重复,从而提高了开发效率和提高测试用例的可维护性。

3、缺点:虽然模块化的步骤相同,但是测试数据不同。比如说重复的登录模块,如果登录用户不同,依旧要重复编写登录脚本。

4.实例:对公共模块,例如登陆和退出进行模块化封装

(3)数据驱动测试

1、概念:它将测试中的测试数据和操作分离,数据存放在另外一个文件中单独维护。

通过数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变。

操作+重复操作+数据分开。

2、优点:

通过这种方式,将数据和重复操作分开,可以快速增加相似测试,完成不同数据情况下的测试。

3、实例从excel表格读取用户名密码,登录邮箱。

二、unittest框架

用Python搭建自动化测试框架,需要组织用例以及测试执行,大部分推荐的是unittest。现在用的也是这个,随着了解,也有其他的框架,有时间再多去学习,保持持续学习哦~

附上官方文档地址:链接描述https://docs.python.org/2.7/library/unittest.html#

unittest是Python自带的单元测试框,可以用来作自动化测试框架的用例组织执行框架。优点:提供用例组织与执行方法;提供比较方法;提供丰富的日志、清晰的报告。
大致流程:

  • 写好TestCase
  • 由TestLoader加载TestCase到TestSuite
  • 然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中。

通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run()来执行,或者可以直接通过TextTestRunner来执行用例。

在Runner执行时,默认将执行结果输出到控制台,我们可以设置其输出到文件,在文件中查看结果。

unittest中最核心的部分是:TestFixture、TestCase、TestSuite、TestRunner。

1、Test fixture

用于一个测试环境的准备和销毁还原。

当测试用例每次执行之前需要准备测试环境,每次测试完成后还原测试环境,比如执行前连接数据库、打开浏览器等,执行完成后需要还原数据库、关闭浏览器等操作。这时候就可以启用testfixture。

  • setUp():准备环境,执行每个测试用例的前置条件;
  • tearDown():环境还原,执行每个测试用例的后置条件;
  • setUpClass():必须使用@classmethod装饰器,所有case执行的前置条件,只运行一次;
  • tearDownClass():必须使用@classmethod装饰器,所有case运行完后只运行一次;

例如:

# 重写TestCase的setUp() tearDown()方法:在每个测试方法执行前以及执行后各执行一次
def setUp(self): # 钩子方法
 print("do something before test : prepare environment")
def tearDown(self): # 钩子方法
 print("do something after test : clean up ")

2、TestCase

类,unittest.TestCase

一个类class继承 unittest.TestCase,就是一个测试用例。一个TestCase的实例就是一个测试用例,就是一个完整的测试流程。

包括测试前环境准备setUp()|setUpClass()、执行代码run()、测试环境后的还原tearDown()|tearDownClass()。
继承自unittest.TestCase的类中,测试方法的名称要以test开头。且只会执行以test开头定义的方法(测试用例)。

例如:【先准备待测试的方法function.py】

#!/usr/bin/python3
# -*- coding:utf-8 -*-
def add(a,b):
 return a+b
def minus(a,b):
 return a-b
def multi(a,b):
 return a*b
def divide(a,b):
 return a/b

【测试脚本】:

import unittest
from A_UnitTest_basicDemo_ok.function import *

class TestFunc(unittest.TestCase):
 # 继承自unittest.TestCase
 # 重写TestCase的setUp()、tearDown()方法:在每个测试方法执行前以及执行后各执行一次
 def setUp(self):
  print("do something before test : prepare environment")

 def tearDown(self):
  print("do something after test : clean up ")
 
 # 测试方法均已test开头,否则是不被unittest识别的
 def test_add(self):
  print("add:")
  self.assertEqual(3,add(1,2))

 def test_minus(self):
  print("minus")
  self.assertEqual(3,minus(5,2))

 def test_multi(self):
  print("multi")
  self.assertEqual(6,multi(2 ,3))

 def test_divide(self):
  print("divide")
  self.assertEqual(2,divide(4,2))

if __name__ == "__main__":
 # 在main()中加verbosity参数,可以控制输出的错误报告的详细程度
 # verbosity=*:默认是1;设为0,则不输出每一个用例的执行结果;2-输出详细的执行结果
 unittest.main(verbosity=2)

或者也可以使用setUpClass() & tearDownClass()方法:

# 如果想在所有case执行之前准备一次测试环境,并在所有case执行结束后再清理环境
@classmethod
def setUpClass(cls):
 print("this setupclass() method only called once")
@classmethod
def tearDownClass(cls):
 print("this teardownclass() method only called once too")

【verbosity】

在测试用例文件的末尾加上如下代码:

if __name__ == "__main__":
 unittest.main(verbosity=2) # 输出详细的错误报告

在unittest.main()中加参数verbosity可以控制错误报告的详细程度:默认为1。0,表示不输出每一个用例的执行结果;2表示详细的执行报告结果。

【执行结果】:输出到控制台

this setupclass() method only called once
test_add (__main__.TestFunc) ... ok
add:
test_divide (__main__.TestFunc) ... ok
divide
test_minus (__main__.TestFunc) ... ok
minus
test_multi (__main__.TestFunc) ... ok
multi

this teardownclass() method only called once too
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

3、TestSuite

上述简单的测试会产生两个问题,可不可以控制test测试用例的执行顺序?若不想执行某个测试用例,有没有办法可以跳过?
对于执行顺序,默认按照test的 A-Z、a-z的方法执行。若要按自己编写的用例的先后关系执行,需要用到testSuite。
把多个测试用例集合起来,一起执行,就是testSuite。testsuite还可以包含testsuite。

一般通过addTest()或者addTests()向suite中添加。case的执行顺序与添加到Suite中的顺序是一致的。

例如:run_main.py

if __name__ == "__main__":
 suite = unittest.TestSuite()
# 定义list,按照list里的顺序执行测试用例
tests=[TestFunc("test_add"),TestFunc("test_minus"),TestFunc("test_multi"),TestFunc("test_divide")]
suite.addTests(tests)
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)

TestSuite可以再包含testsuite,示例如下:

suite1 = module.TheTestSuite()
suite2=module.TheTestSuite()
alltests=unittest.TestSuite([suite1],[suite2])

跳过某个case:skip装饰器

若想让某个测试用例不执行,有没有办法呢?当然是有的,可以使用skip装饰器。

例如:

@unittest.skip("i don't want to run this case -> test_minus() ... ")
def test_minus(self):
 print("minus")
 self.assertEqual(3,minus(5,2))

加上“@unittest.skip()”后,执行看看,对比控制台的输出结果就可以明显看出区别了。

Skip装饰器有如下几种情况:

(1)skip():无条件跳过

@unittest.skip("i don't want to run this case. ")

(2)skipIf(condition,reason):如果condition为true,则 skip

@unittest.skipIf(condition,reason)

(3)skipUnless(condition,reason):如果condition为False,则skip

@unittest.skipUnless(condition,reason)

(4)还可以使用TestCase.skipTest(reason)。例如:

def test_divide(self):
 self.skipTest('do not run test_divide()')
 print("divide")
 self.assertEqual(2,divide(4,2))

控制台输出(部分):test_divide (__main__.TestFunc) ... skipped 'do not run test_divide()'

4、TestLoader

TestLoadder用来加载TestCase到TestSuite中。

loadTestsFrom*()方法从各个地方寻找testcase,创建实例,然后addTestSuite,再返回一个TestSuite实例。

该类提供以下方法:

unittest.TestLoader().loadTestsFromTestCase(testCaseClass)
unittest.TestLoader().loadTestsFromModule(module)
unittest.TestLoader().loadTestsFromName(name,module=None)
unittest.TestLoader().loadTestsFromNames(names,module=None)
unittest.TestLoader().getTestCaseNames(testCaseclass)
unittest.TestLoader().discover()

TestLoader 之discover:

用discover()来加载测试多个测试用例,再用TextRunner类的run()方法去一次执行多个脚本的用例,达到批量执行的效果。

discover方法里面有三个参数:

  • -case_dir:这个是待执行用例的目录。
  • -pattern:这个是匹配脚本名称的规则,test*.py意思是匹配test开头的所有脚本。
  • -top_level_dir:这个是顶层目录的名称,一般默认等于None就行了。

TextTestRunner():执行测试用例。runner.run(test)会执行TestSuite、TestCase中的run(result)方法。

如下:run_main.py示例

import unittest
import os
# 用例的路径
case_path = os.path.join(os.getcwd(),"case")
# 报告存放的路径
report_path = os.path.join(os.getcwd(),"report")
def all_cases():
 discover= unittest.defaultTestLoader.discover(case_path,pattern="test*.py",top_level_dir=None)
 print(discover)
 return discover
if __name__ == "__main__":
 runner = unittest.TextTestRunner(verbosity=2)
 runner.run(all_cases())

5、生成测试报告

生成TXT测试报告

代码示例:

if __name__ == "__main__":
 suite = unittest.TestSuite()
 # 生成.txt的测试报告(控制台的输出写入到文件中)
 suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFunc))
 with open('UnittestTextReport.txt','a') as f:
  runner = unittest.TextTestRunner(stream=f,verbosity=2)
  runner.run(suite)

可以看到在目录下,生成了UnittestTextReport.txt文件。

但是txt格式的文件太过于简陋。我们可以借助与第三方提供的库来输出更加形象的html报告,也可以自定义输出自己想要格式的html格式的报告。

  1. 生成HTML测试报告
  2. 先下载HTMLTestRunner.py(注意Python的版本),http://tungwaiyip.info/software/HTMLTestRunner.html。然后放在Python的Lib目录下;
  3. 在run_main.py文件中加入:from HTMLTestRunner import HTMLTestRunner

HTMLTestRunner()方法有三个参数:

  • --stream:测试报告写入文件的存储区域
  • --title:测试报告的主题
  • --description:测试报告的描述

代码示例:

if __name__ == "__main__":
 suite = unittest.TestSuite()
 # 生成HTML格式的具体测试报告
 with open('HtmlReport.html','wb') as f: # 在python3,要写成'wb' or 'wr'
  runner = HTMLTestRunner(stream=f,title='function test 
report',description='generated by HTMLTestRunner',verbosity=2)
  runner.run(suite)

三、代码示例

function.py

#!/usr/bin/python3
# -*- coding:utf-8 -*-
def add(a,b):
 return a+b
def minus(a,b):
 return a-b
def multi(a,b):
 return a*b
def divide(a,b):
 return a/b

Test_function.py

#!/usr/bin/python3
# -*- coding:utf-8 -*-
import unittest
from UnitTest_1.function import * # from..import ... :要指定文件的路径

class TestFunc(unittest.TestCase): # unittest.TestCase
# 如果想在所有case执行之前准备一次测试环境,并在所有case执行结束后再清理环境
 @classmethod
 def setUpClass(cls):
  print("this setupclass() method only called once")
 @classmethod
 def tearDownClass(cls):
  print("this teardownclass() method only called once too")

 # 测试方法均已test开头,否则是不被unittest识别的
 def test_add(self):
  print("add:")
  self.assertEqual(3,add(1,2))
 def test_minus(self):
  print("minus")
  self.assertEqual(3,minus(5,2))
 # 如果想临时跳过某个case:skip装饰器
 @unittest.skip("i don't want to run this case. ")
 def test_multi(self):
  print("multi")
  self.assertEqual(6,multi(2,3))
 def test_divide(self):
  print("divide")
  self.assertEqual(2,divide(5,2))

if __name__ == "__main__":
 # 在main()中加verbosity参数,可以控制输出的错误报告的详细程度
 # verbosity=*:默认是1;设为0,则不输出每一个用例的执行结果;2-输出详细的执行结果
 unittest.main(verbosity=2)

Test_suite.py

#!/usr/bin/python3
# -*- coding:utf-8 -*-

import unittest
from UnitTest_1.test_function import TestFunc
from HTMLTestRunner import HTMLTestRunner
# 在Python3中已经没有 StringIO,所以引用的时候要注意
from io import StringIO

if __name__ == "__main__":
 suite = unittest.TestSuite()
 # 定义list,按照list里的顺序执行测试用例
 tests = [TestFunc("test_add"),TestFunc("test_minus"),TestFunc("test_multi"),TestFunc("test_divide")]
 suite.addTests(tests)
 '''
 runner = unittest.TextTestRunner(verbosity=2)
 runner.run(suite)
 '''
 
 # 生成.txt的测试报告(控制台的输出写入到文件中)
 '''
 suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFunc))
 with open('UnittestTextReport.txt','a') as f:
  runner = unittest.TextTestRunner(stream=f,verbosity=2)
  runner.run(suite)
 '''

 # 生成HTML格式的具体测试报告
 with open('HtmlReport.html','wb') as f: # 在python3,要写成'wb' or 'wr'
  runner = HTMLTestRunner(stream=f,title='function test report',description='generated by 
     HTMLTestRunner',verbosity=2)
  runner.run(suite)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
浅谈Python黑帽子取代netcat
Feb 10 Python
Python实现决策树C4.5算法的示例
May 30 Python
Python中 map()函数的用法详解
Jul 10 Python
python利用插值法对折线进行平滑曲线处理
Dec 25 Python
OpenCV+Python--RGB转HSI的实现
Nov 27 Python
浅谈ROC曲线的最佳阈值如何选取
Feb 28 Python
pytorch 限制GPU使用效率详解(计算效率)
Jun 27 Python
Python eval函数原理及用法解析
Nov 14 Python
python利用pytesseract 实现本地识别图片文字
Dec 14 Python
python中print格式化输出的问题
Apr 16 Python
Python基于Opencv识别两张相似图片
Apr 25 Python
8g内存用python读取10文件_面试题-python 如何读取一个大于 10G 的txt文件?
May 28 Python
tensorflow实现逻辑回归模型
Sep 08 #Python
Django实现表单验证
Sep 08 #Python
python实现排序算法解析
Sep 08 #Python
TensorFlow实现Logistic回归
Sep 07 #Python
tensorflow实现简单逻辑回归
Sep 07 #Python
Tensorflow使用支持向量机拟合线性回归
Sep 07 #Python
TensorFlow实现iris数据集线性回归
Sep 07 #Python
You might like
微信公众平台DEMO(PHP)
2016/05/04 PHP
thinkphp跨库操作的简单代码实例
2016/09/22 PHP
PHP基于SMTP协议实现邮件发送实例代码
2017/04/27 PHP
定义JavaScript二维数组采用定义数组的数组来实现
2012/12/09 Javascript
js获取IFRAME当前的URL的方法
2013/11/13 Javascript
Javascript学习指南
2014/12/01 Javascript
简易的投票系统以及js刷票思路和方法
2015/04/07 Javascript
jQuery实现折叠、展开的菜单组效果代码
2015/09/16 Javascript
基于jquery实现表格无刷新分页
2016/01/07 Javascript
NodeJS整合银联网关支付(DEMO)
2016/11/09 NodeJs
jQuery图片轮播(二)利用构造函数和原型创建对象以实现继承
2016/12/06 Javascript
如何使用Bootstrap 按钮实例详解
2017/03/29 Javascript
vue.js动态数据绑定学习笔记
2017/05/19 Javascript
详解Nodejs之静态资源处理
2017/06/05 NodeJs
JS写XSS cookie stealer来窃取密码的步骤详解
2017/11/20 Javascript
使用vuex缓存数据并优化自己的vuex-cache
2018/05/30 Javascript
JavaScript日期工具类DateUtils定义与用法示例
2018/09/03 Javascript
Vue + element 实现多选框组并保存已选id集合的示例代码
2020/06/03 Javascript
vue中实现拖动调整左右两侧div的宽度的示例代码
2020/07/22 Javascript
[36:52]DOTA2真视界:基辅特锦赛总决赛
2017/05/21 DOTA
Python随手笔记第一篇(2)之初识列表和元组
2016/01/23 Python
Python实用工具FuckIt.py介绍
2019/07/02 Python
Pytorch模型转onnx模型实例
2020/01/15 Python
日本整理专家Marie Kondo的官方在线商店:KonMari
2020/06/29 全球购物
一套C#面试题
2013/10/09 面试题
体育专业学生自我评价范文
2014/01/17 职场文书
中国梦主题教育活动总结
2014/05/05 职场文书
校园安全演讲稿
2014/05/09 职场文书
小学生九一八纪念日83周年演讲稿500字
2014/09/17 职场文书
群众路线个人剖析材料
2014/10/07 职场文书
2014年幼儿园园务工作总结
2014/12/05 职场文书
运动会广播稿100字
2015/08/19 职场文书
2016年暑假学生家长评语
2015/12/01 职场文书
小学生一年级(书信作文)
2019/08/13 职场文书
golang通过递归遍历生成树状结构的操作
2021/04/28 Golang
python和Appium的移动端多设备自动化测试框架
2022/04/26 Python