unittest单元测试框架

unittest基本简介

unittest是Python自带的一个单元测试框架,unittest又被称为PyUnit,是由Java的JUnit衍生而来,基本结构是类似的。对于单元测试,需要设置预先条件,对比预期结果和实际结果。
由于unittest是Python自带的标准模块,所以不需要单独再去安装。引入包import unittest即可使用。

unittest基本概念

unittest核心的四个概念

test fixture:fixture表示test case运行前需要做的准备工作以及结束后的清理工作。比如,创建临时/代理数据库、目录或启动一个浏览器进程。

test case:test case是单元测试中的最小个体,通常是使用assert方法(断言)检查动作和输入的响应。unittest提供了一个基础类TestCase,一般是基于TestCase类进行扩充,来创建test case测试用例。

test suite:test suite(套件)是test case的合集,通常用test suite将test case按需求汇总后,统一执行。(批量执行用例)

test runner:test runner是一个执行器,用来执行测试用例或者套件。并将测试结果保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。并提供图形界面、文本界面或者返回一个值展示测试结果。

如何创建一个测试类

所有测试用例类都要继承TestCase基本类

1、导入unittest

2、定义一个测试类class,例如:class Test_demo(unittest.TestCase)

test fixture常用的四个方法

1、基于函数级别的方法

  • setup(): 每个测试方法运行前执行一次
  • teardown(): 每个测试方法运行完后执行一次

2、基于类级别的方法

  • setUpClass(): 在测试类执行前执行一次
  • tearDownClass(): 在测试类执行完后执行一次

unittest编写测试方法(用例)规范

  1. py文件需要以test开头。(规范,不必须)
  2. 测试类名称需要Test开头。(规范,不必须)
  3. 每个测试方法名称均以test开头,否则是不被unittest识别的。(规范,必须)
  4. 在unittest框架中,测试用例就是TestCase的实例,所以每个测试类必须继承unittest中TestCase类来编写用例。
  5. 测试方法必须带self参数,用来单独运行或则组合运行用例。
  6. 测试用例的执行顺序是按照测试方法名的ASCII编码字符集的顺序进行排序的。

执行测试脚本

  1. unittest.main():将一个单元测试模块变为可直接运行的测试脚本,main()方法是使用TestLoader类来搜索所有包含在该模块中以test命名开头的测试方法,并自动执行他们。
  2. 执行方法的默认顺序是:根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
 """
1.学习目标
掌握unittest框架下测试用例编写方法
2.操作步骤
2.1 导入unittest
2.2 创建测试类
测试类名称需要Test开头
继承unittest中的TestCase基本类
class Test_demo(unittest.TestCase):
2.3 编写test fixture
setUp()--前置函数
tearDown()--后置函数
setUpClass()--+@classmethod
tearDownClass()+@classmethod
2.4 编写test case
测试方法名称均以test开头
测试用例执行顺序:按照测试用例名称ASCII字符集编码排序。
所以我们在执行测试类中的测试方法的时候,要注意测试方法的执行顺序。
3.需求
编写简单的测试类
"""
# 1 导入unittest
import unittest


# 2 创建测试类
class Test_demo(unittest.TestCase):
# 3 编写test fixture
# setUp我们也称之为前置函数
def setUp(self) -> None:
print("setUp在每个测试用例执行前先执行。")

# setUp我们也称之为后置函数
def tearDown(self) -> None:
print("tearDown在每个测试用例执行后执行。")

@classmethod
# cls等同于self,用于函数和类方便区分。
def setUpClass(cls) -> None:
print("setUpClass在测试类执行前先执行。")

@classmethod
def tearDownClass(cls) -> None:
print("tearDownClass在测试类执行后执行。")

# 4 编写test case
# 每个测试方法均以test开头,否则是不被unittest识别的。
def test_case_03(self):
"""测试用例3,这里是测试用例的备注"""
# 测试方法中,将多行注释写在第一行,就是该方法的备注。
print("执行测试用例3")

def test_case_02(self):
"""测试用例2"""
print("执行测试用例2")

def test_case_01(self):
"""测试用例1"""
print("执行测试用例1")


if __name__ == '__main__':
# 执行当前测试类中,以test开头的所有测试用例
unittest.main()

"""
输出结果:
setUpClass在测试类执行前先执行。
setUp在每个测试用例执行前先执行。
执行测试用例1
tearDown在每个测试用例执行后执行。
setUp在每个测试用例执行前先执行。
执行测试用例2
tearDown在每个测试用例执行后执行。
setUp在每个测试用例执行前先执行。
执行测试用例3
tearDown在每个测试用例执行后执行。
tearDownClass在测试类执行后执行。
"""

测试结果说明

测试执行完成后,会打印如下信息

1
2
3
4
5
# 运行了3个测试用例,执行的时间
Ran 3 tests in 0.008s

# 执行结果
OK

执行结果有如下三种

  • OK :表示测试用例全部通过。
  • F :表示测试用例没通过,代码没有问题。
  • E :表示代码有问题。

断言方法

断言介绍

  1. 在执行测试用例的过程中,最终用例是否执行通过,是通过判断测试得到的实际结果和预期结果是否相等决定的,这时会用到断言方法。
  2. 本着没有消息就是最好的消息的原则,如果断言成功不采取任何措施(不输入任何日志),否则就会触发AssertionError(断言错误)的异常。

常用的断言方法

断言方法名称 使用参数 验证
assertEqual()(常用) a, b, [msg=’测试失败时打印的信息’] 断言 a 和 b 是否相等,相等则测试用例通过
assertNotEqual() a, b, [msg=’测试失败时打印的信息’] 断言 a 和 b 是否不相等,不相等则测试用例通过
assertTrue()(常用) x, [msg=’测试失败时打印的信息’] 断言 x 是否为 True,是 True 则测试用例通过
assertFalse() x, [msg=’测试失败时打印的信息’] 断言 x 是否为 False,是 False 则测试用例通过
assertIs() a, b, [msg=’测试失败时打印的信息’] 断言 a 是否是 b,是则测试用例通过
assertNotIs() a, b, [msg=’测试失败时打印的信息’] 断言 a 是否不是 b,不是则测试用例通过
assertIsNone() x, [msg=’测试失败时打印的信息’] 断言 x 是否为 None,是 None 则测试用例通过
assertIsNotNone() x, [msg=’测试失败时打印的信息’] 断言 x 是否不为 None,不是 None 则测试用例通过
assertIn() a, b, [msg=’测试失败时打印的信息’] 断言 a 是否在 b 中,在 b 中则测试用例通过
assertNotIn() a, b, [msg=’测试失败时打印的信息’] 断言 a 是否不在 b 中,不在 b 中则测试用例通过
assertIsInstance() a, b, [msg=’测试失败时打印的信息’] 断言 a 是否是 b 的一个实例,是则测试用例通过
assertNotIsInstance() a, b, [msg=’测试失败时打印的信息’] 断言 a 是否不是 b 的一个实例,不是则测试用例通过

提示:如果a和b断言失败,则输出msg中定义的信息,如果没有定义msg,则输出系统异常。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
"""
1.学习目标
必须掌握unittest中断言使用
2.语法
2.1 编写位置
在测试用例中去编写,先执行测试用例,最后一行断言。
2.2 使用的断言方法
注意:前边a是预期,后边b是测试实际的值
(1)assertEqual(a,b,msg)
断言a和b是否相等,如果相等,断言成功,否则断言失败
(2)assertTrue(x,msg)
断言条件x是否为True,如果是,断言成功,否则断言失败
(3)其他断言用法类似。
2.3 判定断言结果
断言成功,控制台没有任何提示
断言失败,控制台AssertionError关键字会出现
3.需求
编写一个有断言的测试类
"""
# 1 导入unittest
import unittest


# 2 创建测试类
class Test_demo(unittest.TestCase):

# 3 编写test case
def test_case_03(self):
"""测试用例3"""
print("执行测试用例3")
# 用例步骤执行完成后做断言
# assertEqual断言a和b是否相等
self.assertEqual(2, 1 + 1, msg="断言成功")

"""
执行结果:
断言成功,控制台没有任何提示

下面是总测试结果的日志:
执行测试用例3
# 在0.005秒内进行1次测试
Ran 1 test in 0.005s

# 测试用例全部通过
OK
"""

def test_case_02(self):
"""测试用例2"""
print("执行测试用例2")
# assertEqual断言a和b是否相等
self.assertEqual(3, 1 + 1, msg="断言失败")

"""
执行结果:

执行测试用例2
断言失败
3 != 2
Expected(预期) :2
Actual(实际) :3

下面会有报错信息(主要内容):
AssertionError: 2 != 3 : 断言失败

# 在0.008秒内进行1次测试
Ran 1 test in 0.008s

# 失败一个测试用例
FAILED (failures=1)

# 断言失败
Assertion failed
"""

def test_case_01(self):
"""测试用例1"""
print("执行测试用例1")
# 断言条件x是否为True
self.assertTrue(1 > 2, msg="条件不成立,断言失败")

# 4 编写普通方法
if __name__ == '__main__':
# 执行当前测试类中,以test开头的测试用例
unittest.main()

跳过测试

什么是跳过测试

当测试用例写完后,有些模块有改动时候,会影响到部分用例的执行,这个时候我们希望暂时跳过这些用例。或者前面某个功能运行失败了,后面的几个用例是依赖于这个功能的用例,如果第一步就失败了,后面的用例也就没必要去执行了,为了节省用例执行时间,可选择直接跳过测试。

常用的跳过测试方法和装饰器

当执行有想要跳过的测试,我们可以通过skipskipIfskipUnless装饰器跳过某个测试方法或者测试类。

  1. @unittest.skip(reason):
    skip(reason)装饰器,无条件跳过装饰的测试,并说明跳过测试的原因。
  2. @unittest.skipIf(reason):
    skipIf(condition,reason)装饰器,条件为真时,跳过装饰的测试,并说明跳过测试的原因。
  3. @unittest.skipUnless(reason):
    skipUnless(condition,reason)装饰器,条件为假时,跳过装饰的测试,并说明跳过测试的原因。
  4. @unittest.expectedFailure:
    测试标记为失败。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""
1.学习目标
了解unittest中跳过测试方法使用
2.语法
2.1 放置在需要跳过的测试用例之前
@跳过测试方法
测试用例
2.2 分类
@unittest.skip(跳过原因):表示无条件跳过执行
@unittest.skipIf(判断条件,跳过原因): 当判断条件为真时,跳过测试
@unittest.skipUnless(判断条件,跳过原因):当判断条件为假时,跳过测试
@unittest.expectedFailure: 直接将用例标记为失败
3.需求
编写测试类,使用跳过测试
"""
# 1 导入unittest
import unittest


# 2 创建测试类
class Test_demo(unittest.TestCase):

# 3 编写test case
# 第一条测试用例正常执行
def test_case_01(self):
"""测试用例1"""
print("执行测试用例1")

# 添加skip,不执行测试
@unittest.skip("无条件跳过")
def test_case_02(self):
"""测试用例2"""
print("执行测试用例2")

# 添加skipif,条件为真跳过测试
@unittest.skipIf(True, "条件为真,跳过测试")
def test_case_03(self):
"""测试用例3"""
print("执行测试用例3")

# 添加skipIf,条件为假执行测试
@unittest.skipIf(2 > 3, "条件为假,执行用例")
def test_case_04(self):
"""测试用例4"""
print("执行测试用例4")

# 添加skipUnless,条件为假不执行测试
@unittest.skipUnless(False, "条件为假,跳过测试")
def test_case_05(self):
"""测试用例5"""
print("执行测试用例5")

# 添加skipUnless,条件为真执行测试
@unittest.skipUnless(True, "条件为真,执行用例")
def test_case_06(self):
"""测试用例6"""
print("执行测试用例6")

# 添加expectedFailure,直接将用例标记为失败
@unittest.expectedFailure
def test_case_07(self):
"""测试用例7"""
print("执行测试用例7")

def test_case_08(self):
"""测试用例8"""
print("执行测试用例8")

if __name__ == '__main__':
# 执行当前测试类中,以test开头的测试用例
unittest.main()

TestCase.skipTest()方法

TestCase.skipTest()方法跳过某个测试方法(了解)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1 导入unittest
import unittest

# 2 创建测试类
class TestDmeo(unittest.TestCase):

# 3 编写test case
def test_case_01(self):
"""测试用例1"""
print("执行测试用例1")

# TestCase.skipTest()方法
def test_case_02(self):
"""测试用例2"""
# 跳过测试方法
self.skipTest('跳过用例test_case2')
# 下面这个部分被跳过了
print("执行测试用例2")

if __name__ == '__main__':
# 执行当前测试类中,以test开头的测试用例
unittest.main()

测试套件

测试套件的作用

在我们实际工作,使用unittest框架会有两个问题:

  1. 我们知道测试用例的执行顺序是根据测试用例名称顺序执行的,在不改变用例名称的情况下,我们怎么来控制用例执行的顺序。
  2. 一个测试文件,我们直接执行该文件即可,但如果有多个测试文件,怎么实现多个测试文件一起执行行。

要解决上面两个问题,我们就要用到测试套件TestSuite。

使用测试套件

1)入门用法

用法:

  • unittest.TestSuite():创建测试套件。
  • addTest()addTests() 方法是将测试用例添加到测试套件中。
  • unittest.TextTestRunner():通过该类下面的 run() 方法来运行 suite 所组装的测试用例,suite 测试套件为 run() 方法参数。

例如:test_Demo1 模块下的 TestDmeo 类下的 test_case_01 测试用例添加到测试套件中。

1
2
3
4
5
6
7
8
9
10
# 1.创建测试套件
suite = unittest.TestSuite()

# 2.向测试套件中添加测试用例
# 模块名.测试类名('测试用例名')
suite.addTest(test_Demo.TestDmeo('test_case_01'))

# 3.执行测试套件中的用例
runner = unittest.TextTestRunner()
runner.run(suite)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 1 导入unittest
import unittest

# 2 创建测试类
class TestDmeo(unittest.TestCase):

# 3 编写test case
def test_case_01(self):
"""测试用例1"""
print("执行测试用例1")

def test_case_02(self):
"""测试用例2"""
print("执行测试用例2")

def test_case_03(self):
"""测试用例3"""
print("执行测试用例3")


if __name__ == '__main__':
# 执行当前测试类中,以test开头的测试用例
# unittest.main()

# 1. 创建测试套件
suite = unittest.TestSuite()
# 2. 向测试套件中添加测试用例
# 当前模块中的测试用例,可省略模块名(文件名)
suite.addTest(TestDmeo('test_case_02'))
suite.addTest(TestDmeo('test_case_03'))
suite.addTest(TestDmeo('test_case_01'))
# 3. 执行测试套件中的用例
runner = unittest.TextTestRunner()
runner.run(suite)

提示:向测试套件中添加测试用例的顺序,就是测试用例执行的顺序。(此时解决了第一个问题)

根据不同的条件加载测试用例(了解)

提示:这种方式很少用,了解一下即可。都用下面(3)的方式。

  • unittest.TestLoader():根据不同的条件加载测试用例,其中有几个方法:

    • unittest.TestLoader().loadTestsFromName(测试用例名)
    • unittest.TestLoader().loadTestsFromNames(测试用例名的列表)
    • unittest.TestLoader().loadTestsFromTestCase(测试类名)
    • unittest.TestLoader().loadTestsFromModule(模块名)
    • unittest.TestLoader().discover()

例如:将``test_demo2模块下的TestDmeo类下的test_case_01`测试用例添加到测试套件中。
测试用例名格式:文件名+类名+方法名,一级一级的

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 1 导入unittest
import unittest

# 2 创建测试类
class TestDmeo(unittest.TestCase):

# 3 编写test case
def test_case_0001(self):
"""测试用例0001"""
print("执行测试用例0001")

def test_case_0002(self):
"""测试用例0002"""
print("执行测试用例0002")

def test_case_0003(self):
"""测试用例0003"""
print("执行测试用例0003")


if __name__ == '__main__':
# 1. 创建测试套件
suite = unittest.TestSuite()

# 2. 向测试套件中添加测试用例
"""
# 2.1 loadTestsFromName
# 提示:
name参数是传入文件名,字符串格式
格式:模块名.测试类名.测试用例名
"""
# suite_1 = unittest.TestLoader().loadTestsFromName('test_demo2.TestDmeo.test_case_01')

"""
# 2.2 loadTestsFromNames
参数是一个列表,列表中的元素格式同上
"""
# suite_1 = unittest.TestLoader().loadTestsFromNames(
# ['test_demo2.TestDmeo.test_case_01','test_demo2.TestDmeo.test_case_02'])

"""
# 2.3 loadTestsFromTestCase
参数一个测试类名
当前模块直接传如测试类名称即可
"""
suite_1 = unittest.TestLoader().loadTestsFromTestCase(TestDmeo)

# 加入套件
suite.addTest(suite_1)
# 3. 执行测试套件中的用例
runner = unittest.TextTestRunner()
runner.run(suite)

常用方式

unittest.defaultTestLoader():通过该类下面的discover()方法可自动根据测试目录test_dir匹配查找测试用例文件,如test*.py,并将查找到的测试用例组装到测试套件中。

示例discover=unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')

测试套件示例

  1. 创建test_case包用来存放测试用例。
  2. 以上面两个测试类做例子,把test_demo1test_demo2两个测试用例类文件放入test_case包中。
  3. 编写调用用例脚本run_case.py文件执行多个测试用例。

说明:
test_demo1test_demo2两个文件,就是上面的示例。
下面是run_case.py文件内容,需求是同时执行test_demo1test_demo2两个文件中的测试用例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
"""
1.学习目标
run_case.py必须会写
2.操作步骤
2.1 明确测试用例存放路径
2.2 将要执行的用例添加到测试套件中
2.3 执行测试套件中的用例
3.注意事项
1.run_case.py文件需要放置在项目根目录下
2.所有测试用例都写在test_caset文件夹中
3.编写用例的py文件需要以test开头
"""
# 1.导入unittest
import unittest

# 2.获取测试用例存放路径---测试用例存放文件夹
case_path = "./test_case"

# 3.添加用例到测试套件中
"""
# 如果只添加一个文件,pattern就直接填写文件名
start_dir, 指定case目录
pattern='test*.py', 匹配文件规则,# 选择文件夹中的写test_开头的py文件
"""
discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py")

# 4.执行测试套件中的用例
runner = unittest.TextTestRunner()
runner.run(discover)

参考:http://t.csdnimg.cn/kIqKf