1.1 UnitTest 框架
1.1.1 基础
- 框架
1.框架英文单词framework
2.为解决一类事情的功能集和
> 按照框架的规定去书写代码
- UnitTest 框架
UnitTest是python中自带的一个单元测试框架,用于单元测试,框架不需要额外安装.
UnitTest作用是:用自动化脚本(用例代码)执行框架,使用UnitTest框架来管理运行多个测试用例
- 使用原因
1> 能够组织多个用例去执行
2> 提供丰富的方法(程序代码代替人工自动的判断预期结果与世界结果是否相符)
3> 生成测试报告
- 核心要素(组成)
1> TestCase(最核心模块)
TestCase(测试用例)是一个代码文件,里面书写真正的用例代码
是unittest的组成部分,不是手工和自动化中的测试用例
2> TestSuite(测试套件)
用于管理(组装)多个TestCase(测试用例)
3> TestRunner(测试执行)
用于执行TestSuite(测试套件)
4> TestLoader(测试加载)
用于对TestSuite(测试套件)功能的补充
5> Fixture(测试夹具)----类似于前置条件
书写在TestCase(测试用例)代码中,是一个代码结构,每个方法执行前后都会执行的内容
eg:登录的测试用例(每个用例中重复的代码可以写在Fixture代码结构中,只用写一遍,每次执行用例时都会执行Fixture中的代码,类似于前置条件)
1.打开浏览器2.输入网址
1.1.2 TestCase等测试用例的书写
代码文件依据标识符规则写(将代码的作用在文件开头使用注释说明)
步骤:
1> 导包(unittest)
2> 自定义测试类
3> 在测试类中书写测试方法
4> 执行用例
!!!!注: 运行相关问题:(耗时三天我服了啊)
运行问题参考文档: “Python: Configure Tests“ not found解决方案
注:这个错误改了n遍,放弃!!
参考文档: ERROR conda.cli.main_run:execute(125)
TestSuite & TestRunner
TestSuite(测试套件):管理打包组装TestCase(测试用例)文件的
TestRunner(测试执行):执行TestSuite(套件)
# 步骤----先多创建几个TestCase测试文件
1> 导包
2> 实例化(创建对象)套件对象
3> 使用套件对象添加用例方法
4> 实例化运行对象
5> 使用运行对象去执行套件对象
## TestSuite & TestRunner的使用
# 1> 导包
import unittest
from test_01 import TestDemo22
from test_unittest import TestDemo11
# 2> 实例化(创建对象)套件对象
suite=unittest.TestSuite()
# 3> 使用套件对象添加用例方法,使用方法一:套件对象.addTest(测试类名('方法名'))
suite.addTest(TestDemo22('test1'))
suite.addTest(TestDemo22('test2'))
suite.addTest(TestDemo11('test11'))
suite.addTest(TestDemo11('test22'))
# 3> 使用套件对象添加用例方法,使用方法二: 将一个测试类中的所有方法进行添加
# 套件对象.addTest(unittest.makeSuite(测试类名))
suite.addTest(unittest.makeSuite(TestDemo11))
suite.addTest(unittest.makeSuite(TestDemo22))
# 4> 实例化运行对象
runner =unittest.TextTestRunner() # 固定写法
# 5> 使用运行对象去执行套件对象,运行对象.run(套件对象)
runner.run(suite)
# 示例 在tools模块中定义add函数,对两个数字进行求和计算
# 书写TestCase代码对于add()进行测试
def add(a,b):
return a+b
def func():
print('我是tools 模块中的func函数')
num1=add(1,2)
num2=add(10,20)
num3=add(2,3)
import unittest
from tools import add
class TestAdd(unittest.TestCase):
def test_method1(self):
# 1 2 3
if add(1,2)==3:
print('测试通过')
else:
print('测试不通过')
def test_method2(self):
if add(10,20)==30:
print('测试通过')
else:
print('测试不通过')
def test_method3(self):
if add(2,3)==5:
print('测试通过')
else:
print('测试不通过')
# 套件和指定代码
import unittest
from test_demo import TestAdd
# 2> 实例化套件对象
suite=unittest.TestSuite()
# 添加测试方法
suite.addTest(unittest.makeSuite(TestAdd))
# 实例化执行对象
runner=unittest.TextTestRunner()
runner.run(suite)
TestLoader及Fixture等
是对TestSuite功能的补充,用于组装测试用例
步骤:比如TestCase的代码文件有很多(10 20,30)
1> 导包
2> 实例化测试加载对象并添加用例 --->得到suite对象
3> 实例化运行对象
4> 运行对象执行套件对象
"""textloader使用"""
import unittest
# 2.实例化加载对象---unittest.TestLoader().discover('用例所在的路径','用例代码文件名')
# 用例所在路径建议使用相对路径,用例的代码文件名可以使用*(rf)通配符
# 没discover是加载对象,有discover是suite对象
suite=unittest.TestLoader().discover('./testloader','test_*.py')
# 3.实例化运行对象
runner=unittest.TextTestRunner()
# 4.执行
runner.run(suite)
# 或写成34合并:unittest.TextTestRunner().run(suite)
import unittest
# 1.使用默认的加载对象并加载用例
suite=unittest.defaultTestLoader.discover('testloader','test_*.py')
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):
print('-----5.关闭浏览器')
def test11(self):
print("输入正确的用户名密码验证码,点击登录1")
def test22(self):
print("输入错误的用户名密码验证码,点击登录2")
断言
概念: 程序代替人为判断测试程序执行结果是否符合预期,成功为true,失败为false则抛出异常
在unittest中使用断言,需要通过self.断言方法 来试验
- assertEqual
self.assertEqual(预期结果,实际结果) # 判断预期结果与实际结果是否相等
如果相等,通过,不等则不通过抛出异常
- assertIn
self.assertIn(预期结果,实际结果) # 判断预期结果是否包含在实际结果中
包含用例通过,不包含用例不通过抛出异常
assertIn('admin','admin') # 包含
assertIn('admin','adminnnnn') # 包含
import unittest
from tools import login
class TestLogin(unittest.TestCase):
def test_username_password_ok(self):
self.assertEqual('登录成功',login('admin','123456'))
# 错误的用户名:root,123456,登陆失败
def test_username_error(self):
self.assertEqual('登录失败',login('root','123456'))
# 错误的密码:admin,123123,登录失败
def test_password_error(self):
self.assertEqual('登录失败',login('root','123123'))
# 错误的用户名和密码:aaa,123123,登陆失败
def test_password_uername_error(self):
# 注:两种写法!!!
self.assertEqual('登录失败',login('root','123123'))
self.assertIn('失败',login('root','123123'))
参数化
参数化:在测试方法中,使用变量来代替具体的测试数据,通过传参将测试数据传给方法的变量,减少代码重复
工作场景:测试数据一般在json中,使用代码提取数据--->[(), ()]or[[],[]]
安装插件
unittest框架本身不支持参数化,安装unittest扩展插件parameterzed实现参数化
pip install parameterized
步骤:
1> 导包 unittest /pa
2> 定义测试类
3> 书写测试方法(用到的测试数据使用变量代替)
4> 组织测试数据并传参
import unittest
from parameterized import parameterized # type: ignore
from tools import login
# 组织测试数据
data=[
('admin','123456','登录成功'),
('root','123456','登陆失败'),
('admin','123123','登陆失败')
]
class TestLogin(unittest.TestCase):
# 3.书写测试方法(用到测试数据使用变量代替)
@parameterized.expand(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')