UnitTest框架
官方文档: https://docs.python.org/zh-cn/3/library/unittest.html
1 概念
框架framework是解决一类事情的功能集合
UnitTest是Python自带的一个单元测试框架,能够组织多个用例去执行,提供丰富的断言方法,能够生成测试报告。
2 UnitTest的核心要素
2.1 TestCase
TestCase用于新建测试用例。
2.1.1使用步骤
step1:导入unittest包:import unittest
step2:定义测试类,必须继承unittest.TestCase
step3:定义测试方法,方法名必须以test开头
step4:执行测试用例:
-
(1)使用pycharm在代码上点击鼠标右键,选择使用UnitTest运行
- 运行所有用例: 光标定位到类
- 运行单个用例: 光标定位到测试方法
-
(2)直接调用unittest.main():
if __name__ == '__main__': unittest.main()
举个🌰:
# 导入unittest
import unittest
# 定义测试类
class Test01(unittest.TestCase):
# 定义测试方法
def test_01(self):
print("第一个测试方法")
def test_02(self):
print("第二个测试方法")
# 执行测试方法
if __name__ == "__main__" :
unittest.main()
2.1.2 常见问题解决方案
(1)鼠标右键无法找到Run “TestUnits for XXX”
- 解决方案:
-
step1: 依次点击Preferences…➡️ Tools ➡️ Python Integrated Tools
-
step2: 选择UnitTest后,点击OK
-
step3:重新右键运行
-
(2)报错:AttributeError: module ‘unittest’ has no attribute ‘TestCase’
- 解决方案:检查工程目录中是否存在名为“unittest”的文件,若存在则重命名文件
2.2 TestSuite和TestRunner
TestSuite用于归档需要一起执行的测试。多条测试用例集合在一起就是一个TestSuite。
TestRunner是一个用于执行和输出测试结果的组件。这个运行器可能使用图形接口、文本接口,或返回一个特定的值表示运行测试的结果。
2.2.1 使用步骤
step1:导入unittest包:import unittest
step2:实例化TestSuite:
- suite = unittest.TestSuite()
step3:添加用例:
- 添加指定类中指定的测试方法:suite.addTest(类名(“方法名”))
- 添加指定类中所有以test开头的方法:suite.addTest(unittest.makeSuite(类名))
step4: 执行用例:
- runner = unittest.TextTestRunner()
- runner.run(suite)
2.3 TestLoader和TestRunner
搜索符合条件的TestCase到TestSuite中,即加载满足条件的测试用例,并把测试用例封装成测试套件。
2.3.1 使用步骤
step1: 导入unittest包
step2: 搜索指定目录下指定开头的.py文件,并将查找到的测试用例组装到测试套件中:
suite = unittest.TestLoader().discover(test_dir,pattern=“test*.py”)
或
suite = unittest.defaultTestLoader.discover(test_dir,pattern=“test*.py”)
defaultTestLoader是TestLoader的实例化
- test_dir: 指定的目录文件
- pattern:指定字母开头的模块文件,默认为"test*.py"
step3: 执行用例:
- runner = unittest.TextTestRunner()
- runner.run(suite)
2.4 Fixture
测试脚手架,表示为了开展一项或多项测试所需要进行的准备工作,以及所有相关的清理操作。Fixture是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture。
Fixture有3个控制级别,分别是函数级别,类级别和模块级别
2.4.1 函数级别
初始化:def setUp()
销毁:def tearDown()
每个测试函数执行之前都会执行 setUp,执行之后都会执行tearDwon,运行一次测试方法就会运行一次setUp和tearDown。
2.4.2 类级别
(1)初始化:
@classmethod
def setUpClass(cls)
(2)销毁
@classmethod
def tearDownClass(cls)
测试类运行之前运行一次setUpClass 类,运行之后运行一次tearDownClass类,每个测试类只会运行一次setUpClass和tearDownClass。
2.4.3 模块级别
初始化:def setUpModule()
销毁:def tearDownModule()
模块运行之前执行一次setUpModule ,运行之后运行一次tearDownModule,整个模块只会运行一次setUpModule和tearDownModule
2.4.4 常用场景
(1)初始化:隐式等待、浏览器最大化、获取浏览器实例化对象
(2)结束:关闭浏览器驱动对象
3 断言
3.1 概念
断言是让程序代替人为判断测试程序执行结果是否符合预期结果的过程;因为我们在执行自动化测试的时候,一般都无人值守,所以需要通过断言去检测实际结果与预期是否相符。
3.2 断言的使用方式及常用方法
断言方法定义在unittest.TestCase类中,在定义的测试类中直接调用断言方法:
(1)assertEqual(arg1,arg2,msg=None): 是否相等
(2)assertNotEqual(arg1, arg2,msg=None):是否不等
(3)assertTrue(expr, msg=None):是否为真
(4)assertFalse(expr,msg=None):是否为假
(5)assertIs(arg1, arg2,msg=None):谁否为同一个对象
(6)assertIsNot(arg1, arg2,msg=None):是否不是同一个对象
(7)assertIsNone(expr,msg=None):是否为None
(8)assertIsNotNone(expr,msg=None):是否不为None
(9)assertIn(arg1, arg2,msg=None):arg1是否为arg2的子串
(10)assertNotIn(arg1, arg2,msg=None):arg1是否不为arg2的子串
(11)assertIsInstance(obj, cls,msg=None):obj是否为cls的实例
(12)assertNotIsInstance(obj, cls,msg=None):obj是否不为cls的实例
(13)assertAlmostEqual (first,second, places = 7, msg = None, delta = None):验证first约等于second。 palces: 指定精确到小数点后多少位, 默认为7
(14):assertNotAlmostEqual (first,second, places, msg, delta):验证first不约等于second。 palces: 指定精确到小数点后多少位, 默认为7 注: 在上述的两个函数中,如果delta指定了值, 则first和second之间的差值必须
≤delta。
(15)assertGreater (first, second,msg = None):first 是否大于 second
(16)assertGreaterEqual (first,second, msg = None):first 是否大于等于second
(17)assertLess (first, second,msg = None):first 是否小于 second
(18)assertLessEqual (first,second, msg = None):first 是否小于等于 second
(19)assertRegexpMatches (text,regexp, msg = None):验证正则表达式regexp搜索匹配的文本text。regexp: 通常使用re.search()
(20)assertNotRegexpMatches(text, regexp, msg = None):验证正则表达式regexp搜索不匹配的文本text。regexp: 通常使用re.search() 说明: 两个参数进行比较(>、 ≥、 <、 ≤、 约等、 不约等)
(21)assertListEqual(list1, list2,msg = None):验证列表list1和list2是否相等
(22)assertTupleEqual (tuple1,tuple2, msg = None):验证两个元祖是否相等
(23)assertSetEqual (set1, set2,msg = None):验证两个集合是否相等
(24)assertDictEqual (expected,actual, msg = None):验证两个字典是否相等
以上方法判断结果若为false,会抛出AssertionError异常,可以通过捕获AssertionError异常进行相应的处理:
try:
断言方法
except AssertionError as e:
异常处理
raise e
import time
import unittest
from selenium import webdriver
class TestLogin(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.get("http://127.0.0.1")
self.driver.implicitly_wait(10)
self.driver.maximize_window()
def test_login(self):
self.driver.find_element_by_link_text("登录").click()
self.driver.find_element_by_id("username").send_keys("13032820000")
self.driver.find_element_by_id("password").send_keys("123456")
self.driver.find_element_by_css_selector("[name='sbtbutton']").click()
# 获取错误提示信息
print(self.driver.find_element_by_css_selector(".layui-layer-content").text)
try:
# 断言
self.assertIn("验证码不能为空", msg)
except AssertionError as e:
# 保存截图
self.driver.get_screenshot_as_file("./imgs/img{}.png".format(time.strftime("%Y%m%d-%H%M%S")))
raise e
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
unittest.main()
4 参数化
unittest测试框架,通过安装unittest扩展插件parameterized来实现参数化用户数据驱动,避免写多个方法(冗余)。通过参数的方式来传递数据,从而实现数据和脚本分离,并且可以实现用例的重复执行。
4.1 使用步骤
step1: 安装parameterized插件: pip install parameterized
step2: 导包:from parameterized import parameterized
step3: 使用@parameterized.expand装饰器为测试函数的参数进行参数化
4.2 使用方式
测试两个参数相加:
def add(x, y):
return x + y
4.2.1 方式1
@parameterized.expand([(1, 1, 2), (1, 0, 1), (0, 0, 0)])
def test_add(self, x, y, expect):
print("x={} y={} expect={}".format(x, y, expect))
result = add(x, y)
self.assertEqual(result, expect)
4.2.2 方式2
data = [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
@parameterized.expand(data)
def test_add(self, x, y, expect):
print("x={} y={} expect={}".format(x, y, expect))
result = add(x, y)
self.assertEqual(result, expect)
4.2.2 方式3
def build_data():
return [(1, 1, 2), (1, 0, 1), (0, 0, 0)]
@parameterized.expand(build_data)
def test_add(self, x, y, expect):
print("x={} y={} expect={}".format(x, y, expect))
result = add(x, y)
self.assertEqual(result, expect)
5 把测试函数标记成 跳过
对于一些未完成或不满足测试条件的测试函数和测试类,跳过执行。
5.1 直接将测试函数标记为跳过
@unittest.skip('代码未完成')
5.2 根据条件判断测试函数是否判断
@unittest.skipIf(条件, 描述)
6 生成HTML测试报告
6.1 Export Test Results
step1: 点击Export Test Results小图标
step2: 选择导出的文件格式,以及文件名和存放路径
step3: 查看报告
6.2 HTMLTestRunner
HTMLTestRunner是一个第三方的unittest HTML报告库
step1: 下载HTMLTestRunner.py : http://tungwaiyip.info/software/HTMLTestRunner.html
step2: 导入HTMLTestRunner和unittest包
step3: 生成测试套件
suite = unittest.TestSuite()
suite.addTest(类名("方法名"))
# suite.addTest(unittest.makeSuite(类名))
# suite = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py")
step3: 设置报告生成路径和文件名
file_name = "./report/report.html"
step4: 打开报告
with open(file_name,'wb') as f:
step5: 实例化HTMLTestRunner对象
runner = HTMLTestRunner(stream=f,[title],[description])
stream: 文件流,打开写入报告的名称及写入编码格式)
title:报告标题,可选
description:报告描述信息,可选
step6: 执行测试用例:
runner.run(suite)
import time
import unittest
from HTMLTestRunner import HTMLTestRunner
suite = unittest.defaultTestLoader.discover("./", "test*.py")
report_path = "./report/report{}.html".format(time.strftime("%Y%m%d%H%M%S"))
with open(report_path, "wb") as f:
runner = HTMLTestRunner(stream=f, title="测试报告", description="测试报告的描述信息"
)