Python – unittest单元测试框架(全)

一、简介

1、unittest的作用

unittest是Python内置的单元测试框架,主要用于单元测试,具备编写用例、组织用例、执行用例、输出报告等作用。

2、单元测试框架的优点

单元测试是通过一段代码去验证另一段代码,所以不用单元测试框架也能编写单元测试,但使用框架会更加规范与便捷。单元测试框架的优点:1、提供用例组织与执行;2、提供丰富的断言方法;3、提供丰富的日志。

3、单元测试经常用到的五个概念

  • test case:自动化测试用例,一个TestCase的实例就是一个测试用例;
  • test suite:测试套件,是多个测试用例的集合;
  • testLoader:加载器,用于加载TestCase到TestSuite中。其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例;
  • test runner:执行测试,执行测试用例,并将测试结果保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息;
  • test fixture:测试夹具,一个测试用例的初始化准备及环境还原,主要包含setUp() 和 setDown()方法;

4、unittest的工作原理

首先,编写TestCase完成,由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,最后将运行的结果保存在TextTestResult中


二、Test Case的作用与使用

1、什么是测试用例?

一个TestCase的实例就是一个测试用例,它是一个完整的测试流程。包括测试前准备环境的搭建(SetUP)、实现测试过程的代码(run),以及测试后环境的还原(tearDown)。单元测试(Unittest)的本质也就在这里,一个测试用例就是一个完整的测试单元,通过运行这个测试单元,可以对某一个功能进行验证。

2、手工测试用例与自动化测试用例的区别

手工测试用例:

  • 能通过人为的逻辑判断校验当前步骤的功能实现是否正确。能较好的处理异常场景。
  • 执行测试用例具备一定的跳跃能力。
  • 人工测试可以步步跟踪分析,能够细致的定位问题。
  • 主要用来发现产品缺陷。

自动化测试用例:

  • 所有的判断校验都需要编写脚本来实现。
  • 测试用例步骤之间需要关联关系。
  • 主要用来保证产品主体功能正确完整和让测试人员从繁琐重复的工作中解脱出来。
  • 目前自动化测试阶段定位在冒烟测试和回归测试。

3、testcase实例

编写testcase前:

  • 需先导入unittest包;
  • 实例化一个类;
  • 将实例化的类继承于unittest.TestCase;例:class example1(unittest.TestCase)

代码:

# 使用unittest前,需导入unittest库
import unittest

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
  def setUp(self):
      print('前置函数,每个用例运行前会自动执行一次该函数')

  def tearDown(self):
      print('后置函数,每个用例运行后会自动执行一次该函数')
  
  # 创建测试用例test_a01,用例需以test开头
  def test_a01(self):
      print('测试用例1')

  def test_a02(self):
      print('测试用例2')

# ---------------------------test runner部分-------------------
if __name__ == '__main__':
 	# 执行当前文件的测试用例
 	unittest.main()

执行结果:

前置函数,每个用例运行前会自动执行一次该函数
测试用例1
后置函数,每个用例运行后会自动执行一次该函数

前置函数,每个用例运行前会自动执行一次该函数
测试用例2
后置函数,每个用例运行后会自动执行一次该函数
.
----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

注:创建用例需以test开头,实例类下面一个以test开头的函数就是一条用例。setUp()是测试用例执行前的环境准备,tearDown()是测试用例执行结束后的环境恢复,每个测试用例都会执行一次setUp()和tearDown()。

4、断言

断言是编程术语,表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。测试中,断言一般用于将实际结果与预期结果对比,结果一致返回true,结果不一致返回false。

代码:

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
   def test_a01(self):
       a,b=1,1     # 设置变量a=1,b=1
       self.assertEqual(a,b,'错误提示,a不等于b')   # 设置断言a等于b,不相等报错并输出提示文字
       print('测试用例1')

执行结果:

测试用例1

Ran 1 test in 0.001s

OK

注:断言方法前要加上self,如:self.assertEqual(a,b)

常用断言方法

方法 结果
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
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)

5、跳过用例

在自动化测试中,经常会遇到部分用例无需执行的情况。当用例特别多时,一条一条注释用例或反复添加到testsuit中比较麻烦,这时可以使用unittest中的skip装饰器。

skip装饰器主要有3种:

  • unittest.skip(reason):无条件跳过装饰的用例,并返回跳过的原因reason。
  • unittest.skipIf(condition,reason):condition条件为真时,跳过装饰的用例,并返回跳过的原因reason。
  • unittest.skipUnless(condition,reason):condition条件为假时,跳过装饰的用例,并返回跳过的原因reason。

注:condition表示满足condition条件下则跳过该用例,reason表示跳过的原因。

代码:

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
      def test_a01(self):
     		print('测试用例1')

  	  @unittest.skipIf(1,'当1为true时,跳过test_a02用例')
  	  def test_a02(self):
      	  print('测试用例2')

执行结果:

测试用例1
	
Ran 2 tests in 0.001s

OK (skipped=1)
   
Skipped: 当1为true时,跳过test_a02用例

三、Test Suit的使用

1、testsuit的作用

  • 解决用例执行顺序的问题,让用例按配置的顺序执行
  • 解决用例在多个py文件中分散的问题,将所需用例收集、汇总

2、testsuit实例

在自动化测试中,一般会将测试用例(test case)和执行用例(test runner)的代码分开,写在两个不同的py文件中。这里将测试用例代码写在fff.py文件,将执行测试代码写在run_all.py文件

测试用例(fff.py文件):

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
   def test_a01(self):
      	print('测试用例1')

  	def test_a02(self):
      	print('测试用例2')

执行测试(run_all.py文件):

testsuit使用方法一:

import unittest                     # 导入unittest
from static.fff import example1    # 从 static目录下的fff.py文件中导入example1实例类

# --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(example1('test_a01')) # 添加example1类下的用例test_a01到suite中
suite.addTest(example1('test_a02')) # 添加example1类下的用例test_a02到suite中

# --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner()
runner.run(suite) 					# 将suit放入runner中执行

testsuit使用方法二:

import unittest                     # 导入unittest
from static.fff import example1    # 从 static目录下的fff.py文件中导入example1实例类

# --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()       						 # 构建测试套件
test_cases = [example1('test_a01'),example1('test_a02')] # 将test_a01、test_a02添加到test_cases列表
suite.addTests(test_cases)            					# 将test_cases列表添加到suite中

# --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner()
runner.run(suite) 					# 将suit放入runner中执行

执行结果:

测试用例1
..
测试用例2
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

四、Test Loader的使用

1、testloader的作用

TestLoader(测试加载)一般被用来创建测试套件和搜集testcase,将搜集的testcase添加到testsuit中执行。

testloader提供了以下几种搜集testcase的方法:

方法名 作用
unittest.TestLoader().loadTestsFromTestCase(testCaseClass) 根据testcase类名寻找用例
unittest.TestLoader().loadTestsFromModule(module, pattern=None) 根据testcase所在的文件名寻找用例
unittest.TestLoader().loadTestsFromTestCase(testCaseClass) 根据testcase类名寻找用例
unittest.TestLoader().loadTestsFromName(name, module=None) 根据用例名称寻找用例
unittest.TestLoader().loadTestsFromNames(names,module=None) 根据多个用例名称寻找用例
unittest.defaultTestLoader.discover(start_dir, pattern=‘test*.py’,top_level_dir=None) 根据正则规则匹配路径下符合规则的文件及文件中所有的用例

2、testloader实例

测试用例(fff.py文件):

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
   def test_a01(self):
      	print('测试用例1')

  	def test_a02(self):
      	print('测试用例2')

loadTestsFromTestCase 的使用方法(run_all.py文件):
unittest.TestLoader().loadTestsFromTestCase(testCaseClass)可根据testcase类名寻找用例。testCaseClass:表示test case的类名

import unittest                     # 导入unittest
from testcase.fff import example1    # 从 testcase目录下的fff.py文件中导入example1实例类

# -------------------------test loader部分---------------------
load_case = unittest.TestLoader().loadTestsFromTestCase(example1)   # 加载example1类下的所有用例

# --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(load_case) 			# 添加test load收集到的test case到suite中

# --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner()
runner.run(suite) 					# 将suit放入runner中执行

loadTestsFromModule 的使用方法(run_all.py文件):
unittest.TestLoader().loadTestsFromModule(module, pattern=None)可根据testcase所在的文件名寻找用例。module:表示test case所在的文件名,例如fff.py文件存放测试用例,可导入fff文件并传入使用

import unittest                     # 导入unittest
from testcase.fff import example1    # 从 testcase目录下的fff.py文件中导入example1实例类
from testcaseimport fff             # 从testcase目录下导入fff文件

# -------------------------test loader部分---------------------
load_case = unittest.TestLoader().loadTestsFromModule(fff)          # 加载fff.py文件下的所有用例

# --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(load_case) 			# 添加test load收集到的test case到suite中

# --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner()
runner.run(suite) 					# 将suit放入runner中执行

loadTestsFromName 的使用方法(run_all.py文件):
unittest.TestLoader().loadTestsFromName(name, module=None)可根据用例名称寻找用例。name:表示一个string类型的参数,例如这种格式的:“module.class.method”

import unittest                     # 导入unittest
from testcase.fff import example1    # 从 testcase目录下的fff.py文件中导入example1实例类
from testcaseimport fff             # 从testcase目录下导入fff文件

 # -------------------------test loader部分---------------------
load_case = unittest.TestLoader().loadTestsFromName('fff.example1.test_a01')    # 加载文件名为fff,类名为example1,用例名称为test_a01的用例

 # --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(load_case) 			# 添加test load收集到的test case到suite中

 # --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner()
runner.run(suite) 					# 将suit放入runner中执行

loadTestsFromNames 的使用方法(run_all.py文件):
unittest.TestLoader().loadTestsFromNames(names,module=None)可根据多个用例名称寻找用例。names:表示一个list格式的参数,例:[“module.class.method”]

import unittest                     # 导入unittest
from testcase.fff import example1    # 从 testcase目录下的fff.py文件中导入example1实例类
from testcaseimport fff             # 从static目录下导入fff文件

 # -------------------------test loader部分---------------------
load_case = unittest.TestLoader().loadTestsFromNames(['fff.example1.test_a01']) # 加载列表内文件名为fff,类名为example1,用例名称为test_a01的用例

 # --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(load_case) 			# 添加test load收集到的test case到suite中

 # --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner()
runner.run(suite) 					# 将suit放入runner中执行

执行结果:
前面几个方法加载的用例都是一样的,所以运行的结果也是同一个。

测试用例1
..
测试用例2
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

defaultTestLoader.discover 的使用方法(run_all.py文件):
unittest.defaultTestLoader.discover(start_dir, pattern=‘test*.py’,top_level_dir=None)可根据正则规则匹配路径下符合规则的文件及文件中所有的用例。start_dir:代表指定目录,一般传入存放测试用例的目录路径。从指定目录开始测试模块扫描,仅加载与模式匹配的测试文件,然后返回他们的testSuite对象。pattern:代表匹配testcase文件的规则,可根据正则匹配,默认匹配指定目录下以test开头的py文件。

注:discover()方法可自动根据测试目录start_dir 匹配查找测试用例文件 test*.py ,并将查找到的测试用例组装到测试套件,因此可以直接通过 run() 方法执行 discover

目录结构:
在这里插入图片描述
测试用例(fff_02.py文件):

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example2(unittest.TestCase):
   def test_a03(self):
       print('测试用例3')

run_all.py文件:

import unittest                     # 导入unittest
from static.fff import example1    # 从 testcase目录下的fff.py文件中导入example1实例类
from static import fff             # 从static目录下导入fff文件
import os 

current_directory_path = os.getcwd()    # 获取当前目录的路径,因为用例fff.py文件存放在当前目录下
# -------------------------test loader部分---------------------
load_case = unittest.defaultTestLoader.discover(start_dir=current_directory_path, pattern='fff*.py')    # 加载当前目录下的fff开头的文件下的测试用例

# --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(load_case) 			# 添加test load收集到的test case到suite中

# --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner(verbosity=0)
runner.run(suite) 					# 将suit放入runner中执行

执行结果:

测试用例1
----------------------------------------------------------------------
测试用例2
Ran 3 tests in 0.000s
测试用例3

OK

五、Test Runner的使用

1、test runner的作用

test runner(测试运行器)主要用来运行test case。可配合test suit使用,执行test suit中的用例,并将测试结果保存到TextTestResult实例中。

test runner提供了两种运行testcase的方法:

方法 简介
unittet.main() 执行当前文件中的所有实例类的用例,默认按照test后首字母的ascall码顺序执行
unittest.TextTestRunner().run(test) 按照test suit中存放test case的顺序执行测试用例

2、testloader实例

测试用例(fff.py文件):

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
   def test_a01(self):
      	print('测试用例1')

  	def test_a02(self):
      	print('测试用例2')

unittet.main() 的使用方法(fff.py文件):

unittet.main()可执行当前文件中的所有实例类的用例,默认按照test后首字母的ascall码顺序执行。注意:这个方法一般在用例文件下面添加代码运行

# ---------------------------test runner部分-------------------
if __name__ == '__main__':
   # 执行当前文件的测试用例
   unittest.main()

unittest.TextTestRunner().run() 的使用方法(run_all.py文件):

unittest.TextTestRunner().run(test)一般配合test suit使用,可按照test suit中存放用例的顺序执行测试用例。注:test:表示用例或实例化的test suit。

import unittest                     # 导入unittest
from static.fff import example1    # 从 static目录下的fff.py文件中导入example1实例类

# -------------------------test loader部分---------------------
load_case = unittest.TestLoader().loadTestsFromTestCase(example1)   # 加载example1类下的所有用例

# --------------------------test suit部分-----------------------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(load_case) 			# 添加test load收集到的test case到suite中

# --------------------------test runner部分-------------------------------------
runner = unittest.TextTestRunner(verbosity=0)		#实例化TextTestRunner类
runner.run(suite) 									# 将suit放入runner中执行

执行结果:

测试用例1
----------------------------------------------------------------------
测试用例2
Ran 2 tests in 0.001s

OK

注:
verbosity :表示测试结果的信息详细程,一共三个值,默认是1

  • 0 (静默模式):你只能获得总的测试用例数和总的结果 比如 总共100个 失败20 成功80
  • 1 (默认模式):非常类似静默模式 只是在每个成功的用例前面有个 . 每个失败的用例前面有个 F
  • 2 (详细模式):测试结果会显示每个测试用例的所有相关的信息

六、test Fixture的使用

1、test fixture的作用

测试夹具,用于测试用例环境的搭建和销毁。即用例测试前准备环境的搭建(SetUp前置条件),测试后环境的还原(TearDown后置条件),比如测试前需要登录获取token等就是测试用例需要的环境,运行完后执行下一个用例前需要还原环境,以免影响下一条用例的测试结果。

test fixture(测试夹具)有两种使用方式:

  • 一种是以测试方法为维度的,分别是setUp()和setDown()
  • 一种是以测试类为维度的,分别是:setUpClass()和tearDownClass()

2、前置与后置

setUp()与tearDown(),简称前置与后置,它只会在每个用例执行前后执行一次。

setUp()与tearDown()的使用方法(fff.py文件):

# 使用unittest前,需导入unittest库
import unittest

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
  def setUp(self):
      print('前置函数,每个用例运行前会自动执行一次该函数')

  def tearDown(self):
      print('后置函数,每个用例运行后会自动执行一次该函数')
  
  # 创建测试用例test_a01,用例需以test开头
  def test_a01(self):
      print('测试用例1')

  def test_a02(self):
      print('测试用例2')

# ---------------------------test runner部分-------------------
if __name__ == '__main__':
 	# 执行当前文件的测试用例
 	unittest.main()

执行结果:

前置函数,每个用例运行前会自动执行一次该函数
测试用例1
后置函数,每个用例运行后会自动执行一次该函数

前置函数,每个用例运行前会自动执行一次该函数
测试用例2
后置函数,每个用例运行后会自动执行一次该函数
.
----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

4、类前、后置函数

setupclass、teardownclass,简称类前置、类后置函数,只会在类的前后执行一次,需注意@classmethod的使用。

setUpClass与tearDownClass的使用方法:

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
  '''类前、后置置函数需使用@classmethod装饰'''
  	@classmethod
  	def setUpClass(cls):
      	print('类前置函数,每个测试类运行前会自动执行一次该函数')

  	@classmethod
  	def tearDownClass(cls):
      	print('类后置函数,每个测试类运行后会自动执行一次该函数')

  	def setUp(self):
      	print('前置函数,每个用例运行前会自动执行一次该函数')

  	def tearDown(self):
      	print('后置函数,每个用例运行后会自动执行一次该函数')

   def test_a01(self):
      	print('测试用例1')

  	def test_a02(self):
      	print('测试用例2')

执行结果:

类前置函数,每个测试类运行前会自动执行一次该函数
前置函数,每个用例运行前会自动执行一次该函数
测试用例1
后置函数,每个用例运行后会自动执行一次该函数

前置函数,每个用例运行前会自动执行一次该函数
测试用例2
后置函数,每个用例运行后会自动执行一次该函数
类后置函数,每个测试类运行后会自动执行一次该函数

----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

七、参数化

1、什么是参数化?

参数化:主要是对测试过程中的元素定位、数据进行参数化。在自动化测试脚本的编写过程中,会需要用到数据,而数据通常是变化的、有规律的(如同:计算的公式,公式中的参数和值会变,公式本身不会变,当要对这条公式输入不同参数测试时,这时可以将参数和值进行参数化),使用参数化可以大大减少重复的代码,提高脚本的可复用性。

2、数据驱动与关键字驱动

参数化的两种方式:

  • 数据驱动:指同一段代码,输入不同的数据(参数),得到不同的结果(值)。如:你输入张三得到的结果是帅哥,你输入小雨得到的结果是美女。这种以数据引导代码结果的方式成为数据驱动。
  • 关键字驱动:关键字驱动就是将数据驱动里的数据改为关键字而已,一般用于UI自动化,通过改变关键字,从而改变数据的输入位置,这就叫做关键字驱动。

3、数据驱动实例

unittest 没有自带数据驱动、关键字驱动的功能,所以使用unittest参数化时,可以使用第三方库:ddt或paramunittest,这里使用ddt做演示

环境准备:

安装ddt模块,打开cmd输入

pip install ddt

在这里插入图片描述

ddt的使用方法(fff.py文件):

  • 测试数据为多个字典的list类型
  • 测试类前加修饰@ddt.ddt
  • case前加修饰@ddt.data
 # 使用unittest前,需导入unittest库
import unittest
 # 导入ddt
import ddt

 # -------------------------参数化的参数-------------------
test_data = [{'username':'张三', 'psw':'123456'},
             {'username': '李四', 'psw': '321654'},
             {'username': '王五', 'psw': '654321'},]

 # ---------------------------testcase部分-------------------
 # 实例化example类,并继承于unittest.TestCase
@ddt.ddt
class example1(unittest.TestCase):
    def test_a01(self):
        print('测试用例1')

    @ddt.data(*test_data)
    def test_a02(self, data):
        print('测试用例2')
        print(data)

 # ---------------------------test runner部分-------------------
if __name__ == '__main__':
    # 执行当前文件的测试用例
    unittest.main()

执行结果:

Ran 4 tests in 0.002s

OK



测试用例1

测试用例2
{'username': '张三', 'psw': '123456'}

测试用例2
{'username': '李四', 'psw': '321654'}

测试用例2
{'username': '王五', 'psw': '654321'}

八、生成自动化测试报告

在项目中,自动化用例测试完成后,一般需要生成自动化测试报告。unittest生成自动化测试报告可以使用第三方库:BeautifulReport或HTMLTestRunner,这里使用BeautifulReport做演示。

环境准备:

安装ddt模块,打开cmd输入

pip install BeautifulReport

测试用例(fff.py文件):

# 使用unittest前,需导入unittest库
import unittest

# ---------------------------testcase部分-------------------
# 实例化example类,并继承于unittest.TestCase
class example1(unittest.TestCase):
   def test_a01(self):
      	print('测试用例1')

  	def test_a02(self):
      	print('测试用例2')

BeautifulReport生成测试报告的使用方法(run_all.py文件):

import unittest                     		# 导入unittest
from testcase.fff import example1    		# 从 testcase目录下的fff.py文件中导入example1实例类
from BeautifulReport import BeautifulReport # 导入BeautifulReport库

# -------------------------test loader部分---------------------
load_case = unittest.TestLoader().loadTestsFromTestCase(example1)   # 加载example1类下的所有用例

# ------------------------test suit部分--------------------------
suite = unittest.TestSuite()        # 构建测试套件
suite.addTest(load_case)            # 将test_cases列表添加到suite中

# ------------------------生成测试报告-----------------------------
BeautifulReport(suite).report(filename='测试报告文件名称', description='测试报告标题', report_dir='.') # report_dir='.'把report放到当前目录下

执行结果:

测试用例1
..
测试用例2
测试已全部完成, 可打开 D:\Git\test\dj_test\django\项目\dailyfresh\testcase\测试报告文件名称.html 查看报告

生成的自动化测试报告:
在这里插入图片描述

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注