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()
最后修改:2021 年 08 月 24 日
如果觉得我的文章对你有用,请随意赞赏