1.unittest介绍与使用
# 学习目标
1.掌握TestCase的使用
2.掌握通过TextTestRunner执行TestCase中的测试用例
3.掌握使用TestLoader加载TestCase中的测试用例
4.掌握常用断言assertIn和assertEqual的使用语法
5.了解参数化技术
6.掌握生成HTML格式的测试报告
1.1 Unittest介绍
(1).Unittest组成部分
UnitTest是python的自动化测试框架. Unittest框架主要包含以下部分:
- TestCase(测试用例)
- TestSuite(测试套件, 把多个TestCase集成到一个测试TestSuite)
- TestRunner(执行测试用例)
- TestLoader(自动从代码中加载多个测试用例TestCase)
- Fixture(UnitTest特性)
(2).为什么使用Unittest框架
- 能够组织多个用例去执行
- 提供丰富的断言方法
- 能够生成测试报告(由于生成的测试报告简陋, 我们选择其他插件实现)
1.2 TestCase
定义一个函数, 并对函数进行测试
实现步骤:
- 导包: import unittest
- 定义测试类: 新建测试类必须继承unittest.TestCase
- 定义测试方法: 测试方法名称必须以test开头
- 调用unittest.main()执行测试用例
import unittest
def my_sum(a, b):
return a + b
class MyTest(unittest.TestCase):
# 测试用例01
def test_01(self):
print(my_sum(3, 2))
# 测试用例02
def test_02(self):
print(my_sum(1, 6))
# 测试用例03
def test_03(self):
print(my_sum('abc', 9))
# 测试用例04
def test_04(self):
print(my_sum(0, 4))
1.3 TestSuite
TestSuite可以把多个测试用例整合成一个测试套件
使用方法:
(1).导包: import unittest
(2).导入测试用例文件: 用例文件命名应符合变量命名规则
(3).实例化TestSuite对象: suite = unittest.TestSuite()
(4).调用对象addTest方法: suite.addTest(用例文件名.类名("方法名"))
# 预置条件:
- 创建demo1.py, 内部编写测试用例
- 创建demo2.py, 内部使用测试套件, 加载多个测试用例
# demo1.py
import unittest
def my_sum(a, b):
return a + b
class MyTest(unittest.TestCase):
# 测试用例01
def test_01(self):
print(my_sum(3, 2))
# 测试用例02
def test_02(self):
print(my_sum(1, 6))
# 测试用例03
def test_03(self):
print(my_sum('abc', 9))
# 测试用例04
def test_04(self):
print(my_sum(0, 4))
# demo2.py
import unittest
import demo1
# 实例化TestSuite对象
suite = unittest.TestSuite()
suite.addTest(demo1.MyTest("test_01"))
suite.addTest(demo1.MyTest("test_03")) # 注意: 此操作只是加载测试用例, 并未执行
1.4 makeSuite
上面的suite.addTest()方法, 一次只能加载一条测试用例. 如果自定义的类中测试用例非常多, 且要全部加载.此种方法会非常麻烦, 使用makeSuite方法可以一次性将一个类中的测试用例全部加载进来.
# makeSuite使用示例
import unittest
import demo1
# 实例化TestSuite对象
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(demo1.MyTest)) # 加载demo1中MyTest中所有用例
1.5 TestRunner
TestRunner是用来执行suite对象中加载的测试用例的方法.
# makeSuite使用示例
import unittest
import demo1
# 实例化TestSuite对象
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(demo1.MyTest)) # 加载demo1中MyTest中所有用例
# 实例化TextTestRunner对象
runner = unittest.TextTestRunner()
# 执行suite对象中的用例
runner.run(suite)
1.6 TestLoader
TestLoader可以更方便的加载用例, 使用说明如下:
- TestLoader需要先实例化一个对象, 该对象返回用户一个suite对象
- TestLoader对象可以调用discover方法加载指定文件中的所有用例
- discover方法传递两个参数, 第一个参数为路径, 第二个参数为路径下的文件名, 文件名可以使用通配符, 同时加载指定目录下的多个文件中的全部用例
import unittest
# 实例化TestLoader对象, 调用discover方法加载demo1.py中的全部用例
suite = unittest.TestLoader().discover('./', 'demo1.py')
# 实例化TextTestRunner对象
runner = unittest.TextTestRunner()
# TextTestRunner对象执行suite中所有用例
runner.run(suite)
1.7 TestSuite 与 TestLoader 区别
TestSuite 需要手动添加测试用例(可以添加测试类,也可以添加测试类中某 个测试方法)
TestLoader 搜索指定目录下指定开头.py 文件,并添加测试类中的所有的测试 方法,不能指定添加测试方法;
1.8 Fixture
(1).Fixtrue介绍
# 了解Fixture:
Fixture 是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture. 以开发的视角去理解, Fixture就像是Flask框架中的钩子. 特定的方法, 在测试用例执行前后进行执行.
# Fixture存在控制级别: 不同的级别决定了执行的作用范围和时机
- 方法级别;
- 类级别;
- 模块级别;
(2).不同方法级别特点
a).方法级别
- 初始化(前置处理): def setUp(self) --> 首先自动执行;
- 销毁(后置处理): def tearDown(self) --> 最后自动执行;
运行于测试方法的始末,即:运行一次测试方法就会运行一次setUp和tearDown
import unittest
class my_test01(unittest.TestCase):
def setUp(self):
print("setUP 执行")
def tearDown(self):
print("teardown 执行")
def test_01(self):
print("my_test01 的 test01")
def test_02(self):
print("my_test01 的 test02")
b).类级别
- 初始化(前置处理): @classmethod def setUpClass(cls): --> 首先自动执行
- 销毁(后置处理): @classmethod def tearDownClass(cls): --> 最后自动执行
运行于测试类的始末,即:每个测试类只会运行一次setUpClass和tearDownClass
import unittest
class my_test01(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("setUPClass 执行")
@classmethod
def tearDownClass(cls):
print("tearDownClass 执行")
def setUp(self):
print("setUP 执行")
def tearDown(self):
print("teardown 执行")
def test_01(self):
print("my_test01 的 test01")
def test_02(self):
print("my_test01 的 test02")
c).模块级别(了解)
- 初始化(前置处理): def setUpModule() --> 首先自动执行
- 销毁(后置处理): def tearDownModule() --> 最后自动执行
运行于整个模块的始末,即:整个模块只会运行一次setUpModule和tearDownModule
(3).总结
- 必须继承 unittest.TestCase 类,setUp、tearDown 才是一个 Fixture;
- setUp,tearDown:如果一个类中有多个测试用例,每执行一个测试用例之 前会调用一次 setUp,之后会调用一次 tearDown;
- setUpClass,tearDownClass:如果一个类中有多个测试用例,执行所有测试 用例之前只会调用一次 setUpClass,之后只会调用一次 tearDownClass;
- setUpModule,tearDownModule:只在 import 导入这个模块时会调用一次 setUpModule,模块使用完成之后会调用一次 tearDownModule;
- setUpXXX:一般做初始化工作;
- tearDownXXX:一般做结束工作;
2.断言,参数化与跳过操作
2.1 Unittest断言
(1).常用断言
让程序代替人为判断测试程序执行结果是否符合预期结果的过程。
自动化脚本在执行的时候一般都是无人值守状态,我们不知道执行结果是否 符合预期结果,所以我们需要让程序代替人为检测程序执行的结果是否符合预期 结果,这就需要使用断言。
复杂的断言方法在自动化测试中几乎使用不到,所以我们只需要掌握几个常用的即可。
序号 | 断言方法 | 断言描述 |
---|---|---|
1 | assertTrue(expr, msg=None) | 验证 expr 是 true,如果为 false,则 fail |
2 | assertFalse(expr, msg=None) | 验证 expr 是 false,如果为 true,则 fail |
3 | assertEqual(expected, actual, msg=None) | 验证 expected==actual,不等则 fail |
4 | assertNotEqual(first, second, msg=None) | 验证 first != second, 相等则 fail |
5 | assertIsNone(obj, msg=None) | 验证 obj 是 None,不是则 fail |
6 | assertIsNotNone(obj, msg=None) | 验证 obj 不是 None,是则 fail |
7 | assertIn(member, container, msg=None) | 验证是否 member in container |
8 | assertNotIn(member, container, msg=None) | 验证是否 member not in container |
(2).使用方式
断言方法已经在 unittest.TestCase 类中定义好了,而且我们自定义的测试类已经继承了 TestCase,所以在测试方法中直接调用即可。
import unittest
def my_sum(a, b):
return a + b
class my_test(unittest.TestCase):
def test01(self):
num = my_sum(1, 3)
# 如果 num 为 4,正确
self.assertEqual(4, num)
def test02(self):
num = my_sum(4, 3)
# 如果 num 为 4,正确
self.assertEqual(4, num)
def test03(self):
num = my_sum(1, 2)
# 如果 num 在列表中,正确
self.assertIn(num, [1, 2, 3, 4, 5])
2.2 参数化
通过我们之间对一个两个数加法和函数的验证, 我们会发现, 在编写测试用例的过程中, (1, 3), (4, 3), (1,2)等参数中, 每一组参数都要写一条用例, 且用例代码也是一样形式的, 这就造成了代码冗余. 所以需要进行参数化, 提高用例的复用性.通过参数化的方式来传递数据,从而实现数据和脚本分离。并且可以实现用例的重复执行。
unittest测试框架,本身不支持参数化,但是可以通过安装 unittest 扩展插件 parameterized 来实现。
# 安装:
pip install parameterized
# 使用:
- 导包:from parameterized import parameterized;
- 使用@parameterized.expand 装饰器可以为测试函数的参数进行参数化;
# 使用示例: 方式一
import unittest
from parameterized import parameterized
def my_sum(a, b):
return a + b
class Testmy_sum(unittest.TestCase):
@parameterized.expand([(1, 1, 2), (1, 0, 1), (0, 0, 0)])
def test_01(self, x, y, expect):
result = my_sum(x, y)
self.assertEqual(result, expect)
# 使用示例: 方式二
import unittest
from parameterized import parameterized
def my_sum(a, b):
return a + b
# 构建测试数据
def build_data():
return [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
class Testmy_sum(unittest.TestCase):
@parameterized.expand(build_data)
def test_03(self, x, y, expect):
result = my_sum(x, y)
self.assertEqual(result, expect)
2.3 跳过用例
对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执 行。
# 使用方式:
(1).直接将测试函数标记成跳过
@unittest.skip('代码未完成')
(2).根据条件判断测试函数是否跳过
@unittest.skipIf(condition, reason)
# 示例代码:
import unittest
version = 35
class MyTest1(unittest.TestCase):
@unittest.skip("代码未完成")
def test_01(self):
print("test_01")
@unittest.skipIf(version <= 30, "版本大于 30 才会执行")
def test_02(self):
print("test_02")
@unittest.skip("代码未完成")
class MyTest2(unittest.TestCase):
def test_a(self):
print("test_a")
def test_b(self):
print("test_b")
3.生成HTML测试报告
Unittest自带的TextTestRunner可以实现报告的生成, 但生成的报告比较简陋, 所以我们在此不作讲解, 如果自己有兴趣可以自行科普. 我们会借助HTMLTestRunner插件来生成测试报告.
3.1 生成HTMLTestRunner测试报告
# 实现步骤:
(1).复制 HTMLTestRunner.py 文件到项目文件夹;
(2).导入 HTMLTestRunner、unittest 包;
(3).生成测试套件 suite = unittest.TestLoader().discover("./", "test*.py")
(4).以只写方式打开测试报告文件 f = open("test01.html", "wb") ;
(5).实例化 HTMLTestRunner 对象: runner = HTMLTestRunner(stream=f, title=" 自动化测试报告", description="Chrome 浏览器")
参数说明:
stream:open 函数打开的文件流;
title:[可选参数],为报告标题;
description:[可选参数],为报告描述信息;比如操作系统、浏览器等版本;
(6).执行:runner.run(suite)
(7).关闭文件
# 示例代码
import unittest
from HTMLTestRunner import HTMLTestRunner
# 创建测试套件并加载用例
suite = unittest.TestLoader().discover("./", "test*.py")
# 打开文件
f = open("test01.html", "wb")
# 创建Runner对象
runner = HTMLTestRunner(stream=f, title="自动化测试报告",
description="Chrome 浏览器")
# 执行测试条件并生成测试报告
runner.run(suite)
f.close()