一、 介绍
1、框架
说明:
1. 框架英⽂单词framework
2. 为解决⼀类事情的功能集合
> 需要按照框架的规定(套路) 去书写代码
2、什么是UnitTest框架?
概念:UnitTest是Python⾃带的⼀个单元测试框架,⽤它来做单元测试。
⾃带的框架(官⽅): 不需要单外安装, 只要安装了 Python,就可以使⽤
例如:random, json, os, time
第三⽅框架:想要使⽤ 需要先安装后使⽤(pytest)
例如:selenium,appium,requests
单元测试框架:主要⽤来做单元测试, ⼀般单元测试是开发做的.
对于测试来说, unittest 框架的作⽤是 ⾃动化脚本(⽤例代码) 执⾏框架(使⽤ unittest 框架 来管理运⾏多个测试⽤例的)
3、为什么使⽤UnitTest框架?
1. 能够组织多个⽤例去执⾏
2. 提供丰富的断⾔⽅法(让程序代码代替⼈⼯⾃动的判断预期结果和实际结果是否相符)
3. 能够⽣成测试报告
二、UnitTest核心要素(unitest 的组成部分)
1、
TestCase(最核心的模块)
1、TestCase(测试⽤例), 注意这个测试⽤例是 unittest 框架的组成部分, 不是⼿⼯和⾃动化中我们所说的⽤例(TestCase)
主要作⽤: 每个 TestCase(测试⽤例) 都是⼀个代码⽂件, 在这个代码⽂件中 来书写 真正的⽤例代码
2、TestSuite
TestSuite(测试套件), ⽤来 管理 组装(打包)多个TestCase(测试⽤例) 的
3、TestRunner
TestRunner(测试执⾏,测试运⾏), ⽤来执⾏TestSuite(测试套件)的
4、TestLoader
TestLoader(测试加载), 功能是对 TestSuite(测试套件) 功能的补充,管理 组装(打包)多个 TestCase(测试⽤例) 的
5、Fixture
Fixture(测试夹具), 书写在 TestCase(测试⽤例) 代码中, 是⼀个代码结构, 可以在每个⽅法执⾏前后都会执⾏的内容
例如:
登录的测试⽤例, 每个⽤例中重复的代码就可以写在Fixture 代码结构中, 只写⼀遍, 但每次⽤例⽅法的执⾏,都会执⾏ Fixture 中的代码
1. 打开浏览器
2. 输⼊⽹址
三、TestCase(测试用例)
1. 是⼀个代码⽂件, 在代码⽂件中 来书写真正的⽤例代码
2. 代码⽂件的名字必须按照标识符的规则来书写(可以将代码的作⽤在⽂件的开头使⽤注释说明)
步骤:
1. 导包 (unittest)
2. ⾃定义测试类
3. 在测试类中书写测试⽅法
4. 执⾏⽤例
代码:
"""
代码的⽬的: 学习 TestCase(测试⽤例)模块的书写⽅法
"""
# 1, 导包
import unittest
# 2, ⾃定义测试类, 需要继承 unittest 模块中的TestCase 类即可
class TestDemo(unittest.TestCase):
# 3, 书写测试⽅法, 即 ⽤例代码. ⽬前没有真正的⽤例代码, 使⽤ print 代替
# 书写要求, 测试⽅法 必须以 test_ 开头(本质是以test 开头)
def test_method1(self):
print('测试⽅法 1')
def test_method2(self):
print('测试⽅法 2')
# 4, 执⾏⽤例(⽅法)
# 4.1 将光标放在 类名的后边 运⾏, 会执⾏类中的所有的测试⽅法
# 4.2 将光标放在 ⽅法名的后边 运⾏, 只执⾏当前的⽅法
问题⼀ 代码⽂件的命名不规范
1. 代码⽂件的名字以数字开头
2. 代码⽂件名字中有空格
3. 代码⽂件名字有中⽂
4. 其他的特殊符号
(数字, 字⺟, 下划线组成, 不能以数字开头)
问题 2 代码运⾏没有结果
右键运⾏没有 unittests for 的提示, 出现的问题
解决⽅案:
⽅案1. 重新新建⼀个代码⽂件, 将写好的代码复制进去
⽅案2. 删除已有的运⾏⽅式
问题 3 没有找到⽤例
测试⽅法中不是以 test_ 开头的, 或者单词写错了
四、TestSuite & TestRunner
TestSuite(测试套件): 管理 打包 组装 TestCase(测试⽤例)⽂件的
TestRunner(测试执⾏): 执⾏ TestSuite(套件)
步骤:
1. 导包(unittest)
2. 实例化(创建对象)套件对象
3. 使⽤套件对象添加⽤例⽅法
4. 实例化运⾏对象
5. 使⽤运⾏对象去执⾏套件对象
代码:
TestSuite(测试套件): 是⽤来管理多个 TestCase(测试⽤例) 的,
先创建多个 TestCase(测试⽤例) ⽂件
"""
学习 TestSuite 和 TestRunner 的使⽤
"""
# 1. 导包(unittest)
import unittest
from hm_07_testcase1 import TestDemo1
from hm_07_testcase2 import TestDemo2
# 2. 实例化(创建对象)套件对象,
suite = unittest.TestSuite()
# 3. 使⽤套件对象添加⽤例⽅法
# ⽅式⼀, 套件对象.addTest(测试类名('⽅法名'))
# 建议测试类名和⽅法名直接去复制,不要⼿写
suite.addTest(TestDemo1('test_method1'))
suite.addTest(TestDemo1('test_method2'))
suite.addTest(TestDemo2('test_method1'))
suite.addTest(TestDemo2('test_method2'))
# 4. 实例化运⾏对象
runner = unittest.TextTestRunner()
# 5. 使⽤运⾏对象去执⾏套件对象
# 运⾏对象.run(套件对象)
runner.run(suite)
"""
学习 TestSuite 和 TestRunner 的使⽤
"""
# 1. 导包(unittest)
import unittest
from hm_07_testcase1 import TestDemo1
from hm_07_testcase2 import TestDemo2
# 2. 实例化(创建对象)套件对象,
suite = unittest.TestSuite()
# 3. 使⽤套件对象添加⽤例⽅法
# ⽅式⼆ 将⼀个测试类中的所有⽅法进⾏添加
# 套件对象.addTest(unittest.makeSuite(测试类名))
# 缺点: makeSuite() 不会提示
suite.addTest(unittest.makeSuite(TestDemo1))
suite.addTest(unittest.makeSuite(TestDemo2))
# 4. 实例化运⾏对象
runner = unittest.TextTestRunner()
# 5. 使⽤运⾏对象去执⾏套件对象
# 运⾏对象.run(套件对象)
runner.run(suite)
五、TestLoader (测试加载)
TestLoader (测试加载), 作用和 TestSuite 的作用是一样的, 对 TestSuite 功能的补充,
用来组装测试用例的
比如: 如果 TestCase 的代码文件有很多, (10 20, 30 )
- 使用步骤
1. 导包
2. 实例化测试加载对象并添加用例 ---> 得到的是 suite 对象
3. 实例化 运行对象
4. 运行对象执行套件对象
代码实现:
在一个项目中 TestCase(测试用例) 的代码,一般放在一个单独的目录 (case)
"""
TestLoader 的使用
"""
# 1, 导包
import unittest
# 2, 实例化加载对象并添加用例
# unittest.TestLoader().discover('用例所在的路径', '用例的代码文件名')
# 用例所在的路径,建议使用相对路径, 用例的代码文件名可以使用 *(任意多个任意字符) 通配符
# suite = unittest.TestLoader().discover('./case', 'hm*.py')
# suite = unittest.TestLoader().discover('./case', '*test*.py')
# suite = unittest.TestLoader().discover('./case', '*test*')
suite = unittest.TestLoader().discover('./case', '*case1.py')
# 3, 实例化运行对象
# runner = unittest.TextTestRunner()
# # 4, 执行
# runner.run(suite)
# 可以将 3 4 步 变为一步
unittest.TextTestRunner().run(suite)
# 1. 导包
# 2. 使用默认的加载对象并加载用例
# 3. 实例化运行对象并运行
"""TestLoader 的使用"""
# 1, 导包
import unittest
# 2, 使用默认的加载对象并加载用例
suite = unittest.defaultTestLoader.discover('case', 'hm_*.py')
# 可以将 3 4 步 变为一步
unittest.TextTestRunner().run(suite)
六、Fixture(测试夹具)
Fixture(测试夹具) 是一种代码结构
在某些特定的情况下会自动执行
方法级别[掌握]
在每个测试方法(用例代码)执行前后都会自动调用的结构
# 方法执行之前
def setUp(self):
每个测试方法执行之前都会执行
pass
# 方法执行之后
def tearDown(self):
每个测试方法执行之后都会执行
pass
类级别[掌握]
在每个测试类中所有方法执行前后 都会自动调用的结构(在整个类中 执行之前执行之后个一次)
# 类级别的Fixture 方法, 是一个 类方法
# 类中所有方法之前
@classmethod
def setUpClass(cls):
pass
# 类中所有方法之后
@classmethod
def tearDownClass(cls):
pass
模块级别[了解]
模块: 代码文件
在每个代码文件执行前后执行的代码结构
# 模块级别的需要写在类的外边直接定义函数即可
# 代码文件之前
def setUpModule():
pass
# 代码文件之后
def tearDownModule():
pass
方法级别和类级别的 前后的方法,不需要同时出现,根据用例代码的需要自行的选择使用
案例:
1. 打开浏览器(整个测试过程中就打开一次浏览器) 类级别
2. 输入网址(每个测试方法都需要一次) 方法级别
3. 输入用户名密码验证码点击登录(不同的测试数据) 测试方法
4. 关闭当前页面(每个测试方法都需要一次) 方法级别
5. 关闭浏览器(整个测试过程中就关闭一次浏览器) 类级别
import unittest
class TestLogin(unittest.TestCase):
def setUp(self):
"""每个测试方法执行之前都会先调用的方法"""
print('输入网址......')
def tearDown(self) -> None:
"""每个测试方法执行之后都会调用的方法"""
print('关闭当前页面......')
@classmethod
def setUpClass(cls) -> None:
print('------1. 打开浏览器')
@classmethod
def tearDownClass(cls) -> None:
print('------5. 关闭浏览器')
def test_1(self):
print('输入正确用户名密码验证码,点击登录 1')
def test_2(self):
print('输入错误用户名密码验证码,点击登录 2')
七、断言
让程序代替人工自动的判断预期结果和实际结果是否相符.
断言的结果有两种:
> True, 用例通过
> False, 代码抛出异常, 用例不通过
在 unittest 中使用断言, 都需要通过 self.断言方法 来试验
assertEqual:
self.assertEqual(预期结果, 实际结果) # 判断预期结果和实际结果是否相等
1. 如果相等, 用例通过
2. 如果不相等,用例不通过, 抛出异常
assertIn:
self.assertIn(预期结果, 实际结果) # 判断预期结果是否包含在实际结果中
1. 包含 ,用例通过
2. 不包含, 用例不通过, 抛出异常
assertIn('admin', 'admin') # 包含
assertIn('admin', 'adminnnnnnnn') # 包含
assertIn('admin', 'aaaaaadmin') # 包含
assertIn('admin', 'aaaaaadminnnnnnn') # 包含
assertIn('admin', 'addddddmin') # 不是包含
代码:
import unittest
from tools import login
class TestLogin(unittest.TestCase):
def test_username_password_ok(self):
"""正确的用户名和密码: admin, 123456, 登录成功"""
self.assertEqual('登录成功', login('admin', '123456'))
def test_username_error(self):
"""错误的用户名: root, 123456, 登录失败"""
self.assertEqual('登录失败', login('root', '123456'))
def test_password_error(self):
"""错误的密码: admin, 123123, 登录失败"""
self.assertEqual('登录失败', login('admin', '123123'))
def test_username_password_error(self):
"""错误的用户名和错误的密码: aaa, 123123, 登录失败"""
# self.assertEqual('登录失败', login('aaa', '123123'))
self.assertIn('失败', login('aaa', '123123'))
八、参数化
参数化 在测试方法中, 使用 变量 来代替具体的测试数据, 然后使用传参的方法将测试数据传递给方法的变量
好处: 相似的代码不需要多次书写.
工作中场景:
1. 测试数据一般放在 json 文件中
2. 使用代码读取 json 文件,提取我们想要的数据 ---> [(), ()] or [[], []]
安装插件
unittest 框架本身是不支持 参数化, 想要使用参数化,需要安装插件来完成
- 联网安装(在 cmd 窗口安装 或者 )
pip install parameterized
pip 是 Python 中包(插件) 的管理工具, 使用这个工具下载安装插件
验证
pip list # 查看到 parameterized
参数化代码
1. 导包 unittest/ pa
2. 定义测试类
3. 书写测试方法(用到的测试数据使用变量代替)
4. 组织测试数据并传参
参数化 2
[{"desc": "正确的用户名和密码","username": "admin","password": "123456","expect": "登录成功"},{"desc": "错误的的用户名","username": "root","password": "123456","expect": "登录失败"},{"desc": "错误的的密码","username": "admin","password": "123123","expect": "登录失败"}]
# 1、导包 unittest/ pa
import json
import unittest
from parameterized import parameterized
from tools import login
# 组织测试数据 [(), (), ()] or [[], [], []]
def build_data():
with open('data.json', encoding='utf-8') as f:
result = json.load(f) # [{}, {}, {}]
data = []
for i in result: # i {}
data.append((i.get('username'), i.get('password'), i.get('expect')))
return data
# 2. 定义测试类
class TestLogin(unittest.TestCase):
# 3. 书写测试方法(用到的测试数据使用变量代替)
@parameterized.expand(build_data())
def test_login(self, username, password, expect):
self.assertEqual(expect, login(username, password))
# 4. 组织测试数据并传参(装饰器 @)
八、跳过
对于一些未完成的或者不满足测试条件的测试函数和测试类, 不想执行,可以使用跳过
使用方法, 装饰器完成,代码书写在 TestCase 文件
# 直接将测试函数标记成跳过
@unittest.skip('跳过额原因')
# 根据条件判断测试函数是否跳过 , 判断条件成立, 跳过
@unittest.skipIf(判断条件, '跳过原因')
import unittest
# version = 30
version = 29
class TestDemo(unittest.TestCase):
@unittest.skip('没有什么原因,就是不想执行')
def test_1(self):
print('测试方法 1')
@unittest.skipIf(version >= 30, '版本大于等于 30, 不用测试')
def test_2(self):
print('测试方法 2')
def test_3(self):
print('测试方法 3')
九、测试报告
自带的测试报告
只有单独运行 TestCase 的代码,才会生成测试报告
生成第三方的测试报告
1. 获取第三方的 测试运行类模块 , 将其放在代码的目录中
2. 导包 unittest
3. 使用 套件对象, 加载对象 去添加用例方法
4. 实例化 第三方的运行对象 并运行 套件对象
# 1. 获取第三方的 测试运行类模块 , 将其放在代码的目录中
# 2. 导包 unittest
import unittest
from HTMLTestRunner import HTMLTestRunner
# 3. 使用 套件对象, 加载对象 去添加用例方法
suite = unittest.defaultTestLoader.discover('.', 'hm_05_pa1.py')
# 4. 实例化 第三方的运行对象 并运行 套件对象
# HTMLTestRunner()
# stream=sys.stdout, 必填,测试报告的文件对象(open ), 注意点,要使用 wb 打开
# verbosity=1, 可选, 报告的详细程度,默认 1 简略, 2 详细
# title=None, 可选, 测试报告的标题
# description=None 可选, 描述信息, Python 的版本, pycharm 版本
# file = 'report.html' # 报告的后缀是.html
file = 'report1.html' # 报告的后缀是.html
with open(file, 'wb') as f:
# runner = HTMLTestRunner(f) # 运行对象
runner = HTMLTestRunner(f, 2, '测试报告', 'python 3.6.8 ') # 运行对象
# 运行对象执行套件, 要写在 with 的缩进中
runner.run(suite)
1. 组织用例文件(TestCase 里边), 书写参数化, 书写断言, 书写 Fixture, 书写 跳过, 如果单个测试测试文
件, 直接运行, 得到测试报告, 如果有多个测试文件, 需要组装运行生成测试报告
2. 使用 套件对象组装, 或者使用 加载对象组装
3. 运行对象 运行
3.1 运行对象 = 第三方的运行类(文件对象(打开文件需要使用 wb 方式))
3.2 运行对象.run(套件对象)
import unittest
from HTMLTestRunnerCN import HTMLTestReportCN
# 组装用例方法
suite = unittest.defaultTestLoader.discover('.', '*pa1.py')
# 实例化运行对象
with open('report_cn.html', 'wb') as f:
runner = HTMLTestReportCN(f)
runner.run(suite)