目录
UnitTest框架基础
UnitTest是Python⾃带的⼀个单元测试框架,⽤它来做单元测试,不需要单外安装。
单元测试框架: 主要⽤来做单元测试, ⼀般单元测试是开发做的。
对于测试来说, unittest 框架的作⽤是⾃动化脚本(⽤例代码) 执⾏框架(使⽤ unittest 框架来管理运⾏多个测试⽤例的)
为什么使⽤UnitTest框架:
1. 能够组织多个⽤例去执⾏。
2. 提供丰富的断⾔⽅法(让程序代码代替⼈⼯⾃动的判断预期结果和实际结果是否相符)。
3. 能够⽣成测试报告。
UnitTest核心要素(unitest 的组成部分):
1. TestCase(最核⼼的模块):TestCase(测试⽤例), 这个测试⽤例是 unittest 框架的组成部分, 不是⼿⼯和⾃动化中我们所说的⽤例(Test Case)。
主要作⽤: 每个 TestCase(测试⽤例) 都是⼀个代码⽂件, 在这个代码⽂件中书写真正的⽤例代码。
2. TestSuite:TestSuite(测试套件), ⽤来管理组装(打包)多个TestCase(测试⽤例) 。
3. TestRunner:TestRunner(测试执⾏,测试运⾏), ⽤来执⾏TestSuite(测试套件)。
4. TestLoader:TestLoader(测试加载), 功能是对 TestSuite(测试套件)功能的补充,管理,组装(打包)多个TestCase(测试⽤例) 的
5. Fixture:Fixture(测试夹具), 书写在 TestCase(测试⽤例) 代码中, 是⼀个代码结构, 可以在每个⽅法执⾏前后都会执⾏的内容。
TestCase(测试⽤例)
TestCase(测试⽤例)代码⽂件的名字必须按照标识符的规则来书写(可以将代码的作⽤在⽂件的开头使⽤注释说明)。
例子:
先定义一个用来测试的add_test.py文件:
#add函数
def add(a,b):
c =a + b
return c
下面是TestCase(测试⽤例)代码test_add_test.py文件:
"""
代码的⽬的: 学习 TestCase(测试⽤例)模块的书写⽅法
"""
# 导包
import unittest
# 导入测试代码
from add_test import add
# 测试add_test的类
# ⾃定义测试类, 需要继承 unittest 模块中的TestCase 类即可
class addtest(unittest.TestCase):
# 书写要求:测试⽅法 必须以 test_ 开头(本质是以test 开头)
# 学习断言后不用if和print做判断
def test_add(self):
if add(1,2) == 3:
print("测试通过")
else:
print("测试不通过")
def test_add02(self):
if add(1,2) == 6:
print("测试通过")
else:
print("测试不通过")
def test_add03(self):
if add(2.1,2) == 4.1:
print("测试通过")
else:
print("测试不通过")
# 执⾏⽤例(⽅法)
# 将光标放在 类名的后边运⾏, 会执⾏类中的所有的测试⽅法
# 将光标放在 ⽅法名的后边运⾏, 只执⾏当前的⽅法
下面是测试结果:
如果不能运行问题:
1.代码⽂件的命名不规范,必须按照标识符的规则来书写。
2.代码运⾏没有结果右键运⾏没有 unittests for 的提示(将光标放在类名的后边运⾏, 会执⾏类中的所有的测试⽅法。将光标放在 ⽅法名的后边运⾏, 只执⾏当前的⽅法)。解决⽅案:(1)重新新建⼀个代码⽂件, 将写好的代码复制进去(2)删除已有的运⾏⽅式。
3.没找到用例,测试⽅法中不是以 test_ 开头的, 或者单词写错了。
TestSuite & TestRunner
TestSuite(测试套件):管理,打包,组装TestCase(测试⽤例)
TestRunner(测试执⾏):执⾏TestSuite(套件)
下面是将TestCase(测试⽤例)代码打包运行add_test_suite&runner.py文件:
"""
TestSuite 和 TestRunner 的使⽤
"""
# 导包(unittest)
import unittest
# 导入testcase文件
from test_add_test import addtest
# 实例化(创建对象)套件对象
suite = unittest.TestSuite()
# 套件对象.addTest(测试类名('⽅法名'))
suite.addTest(addtest('test_add'))
suite.addTest(addtest('test_add02'))
suite.addTest(addtest('test_add03'))
# 实例化运⾏对象
runner = unittest.TextTestRunner()
# 使⽤运⾏对象去执⾏套件对象
# 运⾏对象.run(套件对象)
runner.run(suite)
⽅式⼆:将⼀个测试类中的所有⽅法进⾏添加
import unittest
from test_add_test import addtest
# 实例化(创建对象)套件对象
suite = unittest.TestSuite()
# 将⼀个测试类中的所有⽅法进⾏添加
# 套件对象.addTest(unittest.makeSuite(测试类名))
# 缺点: makeSuite() 不会提示
suite.addTest(unittest.makeSuite(addtest))
# 实例化运⾏对象
runner = unittest.TextTestRunner()
# 运⾏对象去执⾏套件对象
runner.run(suite)
TestLoader (测试加载)
TestLoader (测试加载), 作用和 TestSuite 的作用是一样的, 对 TestSuite 功能的补充,用来组装测试用例的。当TestCase 的代码文件有很多的时候,使用TestLoader (测试加载)。
import unittest
# 实例化加载对象并添加用例
# unittest.TestLoader().discover('用例所在的路径', '用例的代码文件名')
# 用例所在的路径,建议使用相对路径, 用例的代码文件名可以使用 *(任意多个任意字符) 通配符
# 可以执行同一个路径下的多个测试用例文件
# 下面代码意思是当前路径下以 test_ 开头的所有TsetCase(测试用例)文件
suite = unittest.TestLoader().discover(".","test_*.py")
# 实例化运行对象
runner = unittest.TextTestRunner()
# 执行
runner.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
方法级别和类级别的 前后的方法,不需要同时出现,根据用例代码的需要自行的选择使用。
案例:登录测试。test_Fixture.py文件。
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(a, b) | a == b | 验证a是否等于b |
assertIn(a, b) | a in b | 验证a是否是b的子串 |
这里简单示范两个,用法都一样。更多断言可以上网查找。
先定义一个用来测试的test_tools.py文件:
def login(username, password):
if username == 'admin' and password == '123456':
return '登录成功'
else:
return '登录失败'
下面是断言代码test_assert.py文件:
import unittest
from test_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, 登录失败
这里断言成功是因为 assertIn 验证a是否是b的子串
失败是登录失败的子串
"""
self.assertIn('失败', login('aaa', '123123'))
下面是断言测试结果:
这里有一个断言失败,第17行,原因注释有写。
参数化
参数化:在测试方法中, 使用变量来代替具体的测试数据, 然后使用传参的方法将测试数据传递给方法的变量。
unittest 框架本身是不支持参数化, 想要使用参数化,需要安装插件来完成(cmd窗口)
pip install parameterized
在实际工作中测试数据一般放在 json 文件中,使用代码读取 json 文件,提取我们想要的数据 为这种形式---> [(), ()] or [[], []]
下面是参数化代码文件:
import unittest
from parameterized import parameterized
from test_tools import login
# 组织测试数据 [(), (), ()] or [[], [], []]
data = [
('admin', '123456', '登录成功'),
('root', '123456', '登录失败'),
('admin', '123123', '登录失败')
]
# 定义测试类
class TestLogin(unittest.TestCase):
# 书写测试方法(用到的测试数据使用变量代替)
# 组织测试数据并传参(装饰器 @)
# 数据和参数一一对应
@parameterized.expand(data)
def test_login(self, username, password, expect):
self.assertEqual(expect, login(username, password))
跳过
对于一些未完成的或者不满足测试条件的测试函数和测试类, 不想执行,可以使用跳过。使用方法, 装饰器完成。代码书写在 TestCase 文件中。
# 直接将测试函数标记成跳过
@unittest.skip('跳过额原因')
# 根据条件判断测试函数是否跳过 , 判断条件成立, 跳过
@unittest.skipIf(判断条件, '跳过原因')
下面是跳过的示例test_skip.py:
import unittest
# version = 29
version = 31
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 的代码,才会生成测试报告。(比较简陋)
在pycharm中鼠标点三个点,可以看见。老版本将运行窗口往右拉可以看见。
生成第三方的测试报告:获取第三方的 测试运行类模块 , 将其放在代码的目录中。
需要HTMLTestRunner.py文件或者HTMLTestRunnerCN.py(中文版)文件
下面是中文版测试报告示例test_report_cn.py文件:
import unittest
from HTMLTestRunnerCN import HTMLTestReportCN
# 使用套件对象, 加载对象 去添加用例方法
suite = unittest.defaultTestLoader.discover('.', 'test_assert.py')
# 4. 实例化 第三方的运行对象 并运行 套件对象
# HTMLTestRunner()
# stream=sys.stdout, 必填,测试报告的文件对象(open ), 注意点,要使用 wb 打开
# verbosity=1, 可选, 报告的详细程度,默认 1 简略, 2 详细
# title=None, 可选, 测试报告的标题
# description=None 可选, 描述信息, Python 的版本, pycharm 版本
file = 'report.html' # 报告的后缀是.html
with open(file, 'wb') as f:
# runner = HTMLTestRunner(f) # 运行对象
runner = HTMLTestReportCN(f, 2, '测试报告', 'python 3.6.8 ') # 运行对象
# 运行对象执行套件, 要写在 with 的缩进中
runner.run(suite)
得到report.html文件,用浏览器打开: