Python框架之UnitTest

image-20230504104145035

UnitTest框架测试

image-20230504120042714

一. TestCase

1
2
每一个TestCase(测试用例)都是一个代码文件, 在其中来书写真正的测试用例
代码必须按照标识符的规则来书写
  • 步骤
1
2
3
4
导包
自定义测试类
在测试类中书写测试用例
执行用例
  • 代码实现
1
2
3
4
5
6
7
8
9
10
11
12
import unittest

class TestDemo(unittest.TestCase):
'自定义测试案例类,需要继承测试模块中中的TestCase类即可'

# 测试方法就是我们需要的用例代码
# 暂时我们用print代替
def test_method1(self):
print("测试方法一")

def test_method2(self):
print("测试方法二")
  • 常见的错误
1
2
3
4
5
代码文件的名字以数字开头
代码文件名字中有空格
代码文件中有中文
其他特殊符号
(数字、字母、下滑线组成,不能以数字开头)

二. 使用UnitTest中的TestSuite管理测试用例TestCase

1
2
3
4
步骤:
导包
实例化套件对象(TestSuite)
使用套件对象添加用例方法
1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 导包
import unittest

from RayStuDayOne.testCxase import TestDemo

# 2. 实例化套件对象
suite = unittest.TestSuite()
# 3. 使用套件对象添加用例方法
suite.addTest(TestDemo('test_method1'))
suite.addTest(TestDemo('test_method2'))
# 这里也可以用下面的方法,只是idea没有提示
suite.addTest(unittest.makeSuite(类名))

1
2
3
注意 :
1. test方法必须以 test_ 开头,不然就不会生成测试方法,也就不会执行
2.

三. 使用TestRunner执行测试套件(TestSuite)

1
2
testSuite : 作用是打包管理测试用例(testCase)
TestRunner : 执行TestSuite(套件)
  • 步骤
1
2
3
导包
实例化运行对象
使用运行对象去执行套件对象
  • 实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 导包
import unittest

from RayStuDayOne.testCxase import TestDemo

# 2. 实例化套件对象
suite = unittest.TestSuite()
# 3. 使用套件对象添加用例方法
suite.addTest(TestDemo('test_method1'))
suite.addTest(TestDemo('test_method2'))
# 这里也可以用下面的方法,只是idea没有提示
suite.addTest(unittest.makeSuite(类名))
# 4. 实例化运行对象
runner = unittest.TextTestRunner()
# 5. 使用运行对象去执行套件对象 ,里面写套件对象
runner.run(suite)

image-20230504111348398

整体测试实现:

  • 首先明确需求
1
2
需求 :
完成对add方法的测试
1
2
def add(a, b):
return a + b
  • 书写TestCase测试用例
1
2
3
4
5
6
    
步骤
导包
自定义测试类
在测试类中书写测试用例

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
import unittest

from Demo.tools import add

class testAdd(unittest.TestCase):
"""自定义测试类,实现测试方法的书写"""

def test_demo(self):
# 比较实际结果 和 预期结果是否相等
if add(1, 2) == 3:
print("测试通过!") # 现在暂时是用打印, 以后是直接靠断言
else:
print("测试不通过!")

def test_demo1(self):
# 比较实际结果 和 预期结果是否相等
if add(10, 11) == 22:
print("测试通过!")
else:
print("测试不通过!")

def test_demo2(self):
# 比较实际结果 和 预期结果是否相等
if add(11, 2) == 13:
print("测试通过!")
else:
print("测试不通过!")

  • 使用测试套件(TestSuite)来管理测试用例
1
2
3
4
步骤
导包
实例化套件对象(TestSuite)
使用套件对象添加用例方法
  • 使用测试执行 来执行测试套件
1
2
实例化运行对象
使用运行对象去执行套件对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 导包
import unittest

from Demo.testDemo import testAdd

# 2. 使用Suite 和 Runner 管理testAdd(unittest.TestCase)文件

# 3. 实例化套件对象
suite = unittest.TestSuite()

# 4. 添加测试方法
suite.addTest(unittest.makeSuite(testAdd))

# 5. 实例化执行对象
runner = unittest.TextTestRunner()

# 6. 执行测试套件
runner.run(suite)

TestLoader(测试加载)

1
2
3
4
5
6
7
8
9
10
11
12
对TestSuite进行补充
管理多个TestCase

比如:
如果TestCase的代码文件有很多(10,20,30)

步骤:
1. 导包
2. 实例化测试加载对象 并添加测试用例 ---- 》 得到的是suite对象
3. 实例化运行对象
4. 运行对象执行套件对象

  • 代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 导包
import unittest

import Case

# 2. 实例化加载对象 ,并添加用例
# discover 就是发现这个加载, 就可以将这个加载 作为suite对象来使用,同时 discover就可以按照通配符来进行匹配
# 比如我们这里匹配的就是Case目录下 的 以test开头的py文件
suite = unittest.TestLoader().discover('Case', 'test*.py')

# 3. 使用测试运行 来运行测试loader
runner = unittest.TextTestRunner()
runner.run(suite)

还可以简化

image-20230504122758635

Fixtrue ( 测试夹具)

1
2
3
4
5
是一种 代码结构
在某种特定情况下, 会自动执行

需要就写 ,不需要就不用写

  • 方法级别
1
2
在每个测试方法(用例代码) 执行前后都会自动调用的结构

1
2
3
4
5
6
7
8
9
def setup(self):
# 在每个测试方法执行之前都会执行
pass


def teardown(self):
# 在每个测试方法执行之后都会执行
pass

  • 类级别
1
在每个测试类中所有方法执行前后, 都会自动调用的结构(在整个类中, 执行之前执行之后各一次)
1
2
3
4
5
6
7
8
9
10
# 类级别的Fixture方法 , 是一个类方法
# 类中所有方法之前
@classmethod
def setupClass(cls):
pass


@classmethod
def tearDown(self):
pass
  • 模块级别

image-20230504123752386

案例

1
2
3
4
5
6
7
打开浏览器 ------------类级别
输入网址 ------------方法级别
输入用户名密码 验证吗点击登录 ---------测试方法
关闭当前页 ------------方法级别
关闭浏览器------------类级别
+++++++++++++++++++++++++++++++++++++
一值重复上述的结构
  • 执行

image-20230504124246887

image-20230504124332819

  • 运行结果

image-20230504124345218

断言

断言的定义

  1. 断言的定义

概念: 断言就是让程序代替人为判断测试程序执行结果是否符合预期结果的过程。

  1. 断言的意义
1
2
实现自动化测试

  1. 断言结果
1
2
3
4
5
True: 
用例通过

False:
用例未通过

常见的断言方法

序号 断言方法 断言描述
1 assertTure(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) 验证first != second ,相等为 fail

。。。。有很多

  • 使用方法:
1
2
3
4
5
6
7
self.aseertEqual(预期结果,实际结果) 
相等 ,则预期通过。不相等则是不通过。

self.assertIn(预期结果,实际结果)
判读预期结果是否包含在实际结果中 实际>预期
包含则为true

  • 使用断言来比较之前的测试
1
2
3
4
5
6
7
8
9
10
11
12
    def test_demo(self):
# 比较实际结果 和 预期结果是否相等
self.assertEqual(3, add(1, 2))
# 用上面的代替下面的

def test_demo1(self):
# 比较实际结果 和 预期结果是否相等
if add(10, 11) == 22:
print("测试通过!")
else:
print("测试不通过!")

他不会生成结果,如果测试失败他就不会通过,如果成功, 那么就会显示ok, 然后通过下面的步骤就可以导出测试报告。

image-20230504152033984

参数化

1
2
3
4
5
6
7
参数化 在测试方法中, 通过使用 变量来代替具体的测试数据, 然后使用传参的方法将
数据传递给方法变量。
好处 : 相似的代码不需要重复读写

日常场景:
测试数据一般放在json数据中
使用读取json文件, 提取我们需要的数据

安装相关的插件

通过终端 : 输入 pip install parameterized即可

步骤

1
2
3
4
5
6
步骤: 
1. 导包
2. 定义测试类
3. 书写测试方法(用到的测试数据使用变量代替)
4. 组织测试数据并传参 (数据和顺序需要保证一致)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 导包
import unittest
from parameterized import parameterized
from Demo.tools import add

# 2. 定义测试类 ,书写测试方法 。(用到的测试数据使用变量代替)

class testAddOne(unittest.TestCase):
"""自定义测试类,实现测试方法的书写"""


@parameterized.expand(data) # 使用装饰器的方法传参
def test_demo(self, paramA, paramB, expect):
# 比较 预期结果 and 实际结果 是否相等
self.assertEqual(expect, add(paramA, paramB))



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
组织测试数据并传参

# 组织测试数据
组织数据的格式
# [(),(),()....] 或者 [[],[],[],[]....]
data = [
(1, 2, 3),
(11, 21, 32),
...
]

传参通过装饰器的方法(也就是Java中的注解形式)
@parameterized.expand(data) # 使用装饰器的方法传参
def test_demo(self, paramA, paramB, expect)


参数化二

通过导入测试化数据的方式来完成测试

  1. 导入测试化数据

image-20230504170314546

  1. 完成对数据的读取和转换(通过定义方法完成)
1
2
3
4
5
6
7
8
9
10
def build_Data():
data = [] # 用于接收数据
with open('H:\pythonStudy\Case\data.json', encoding='UTF-8') as f:
result = json.load(f) # f中是列表里面套元组的过程, 而我们需要的是列表里面套列表的形式
for i in result: # i 就是里面的每个数据 { XXX }
# 将数据存储到data中, 然后作为组织测试数据返回
data.append((i.get('param1'), i.get('param2'), i.get('param3')))
return data


  1. 在自定义测试类中,通过装饰器传参导入数据
1
2
3
4
5
6
7
8
class testAddOne(unittest.TestCase):
"""自定义测试类,实现测试方法的书写"""

@parameterized.expand(build_Data()) # 使用装饰器的方法传参
def test_demo(self, paramA, paramB, expect):
# 比较 预期结果 and 实际结果 是否相等
self.assertEqual(expect, add(paramA, paramB))

跳过

1
对一些未完成的 ,或者不满足测试条件的测试函数和测试类,可以跳过执行

使用方法

1
2
3
4
5
6
直接将测试函数标记成为跳过
@unittest.skip('代码为完成')


# 根据条件判断测试函数是否跳过
@unittest.skipIf(condition,reason)

实现

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
class testAddOne(unittest.TestCase):
"""自定义测试类,实现测试方法的书写"""
version = 30

# @unittest.skipIf()
@parameterized.expand(build_Data()) # 使用装饰器的方法传参
def test_demo(self, paramA, paramB, expect):
# 比较 预期结果 and 实际结果 是否相等
self.assertEqual(expect, add(paramA, paramB))

@unittest.skipIf(version >= 30, '版本号大于等于30 ,不用测试')
def test_demo1(self):
# 比较实际结果 和 预期结果是否相等
if add(10, 11) == 22:
print("测试通过!")
else:
print("测试不通过!")

@unittest.skip("不想测试了")
def test_demo2(self):
# 比较实际结果 和 预期结果是否相等
if add(11, 2) == 13:
print("测试通过!")
else:
print("测试不通过!")

image-20230504172724209

测试报告的生成

1
2
只有testCase的才能生成 ,使用testSuite 和 testRunner包装的套件对象无法生成

使用第三方的类库生成报告

1
2
3
4
5
将第三方的测试运行类模块放在当前代码的目录中
步骤 :
1. 导包unittest
2. 使用套件对象 ,加载对象, 去添加测试用例
3. 实例化第三方的运行对象, 并运行套件对象