" Python为开发者提供了内置的单元测试框架 unittest
,它是一种强大的工具,能够有效地编写和执行单元测试。unittest
提供了完整的测试结构,支持自动化测试的执行,能够对测试用例进行组织,并且提供了丰富的断言方法。最终,unittest
会生成详细的测试报告,这个框架非常简单且易于使用。"
unittest
核心概念
在 unittest
中,有四个核心概念:
-
TestCase(测试用例):每个测试用例实例用于封装一个或多个测试函数。
-
TestSuite(测试套件):这是多个测试用例的集合,用于组织和执行多个测试用例。
-
TestLoader(测试加载器):这是一个用于将测试用例加载到测试套件中的工具。
-
TextTestRunner(测试运行器):这是用于执行测试用例的运行器,负责运行测试并生成结果报告。
-
Fixture(环境管理机制):这是测试用例的环境搭建和销毁部分,包括前置条件和后置条件。
unittest
的工作流程
-
编写继承自
unittest.TestCase
的测试用例类,其中每个测试函数都是一个独立的测试用例。 -
使用
TestLoader
加载测试用例,并将它们组织成TestSuite
对象。 -
使用
TestRunner
运行TestSuite
中的测试用例,并输出测试结果。
使用unittest
初级指南
-
导入
unittest
模块以及被测试的文件或类。 -
创建一个测试类,并继承
unittest.TestCase
,所有自定义的单元测试类都要继承它,作为基类。 -
重写
setUp
和tearDown
方法,用于初始化和清理测试环境(如果有必要)。 -
定义测试函数,函数名以
test_
开头,这样才能被识别并执行。 -
在测试函数中使用断言来判断测试结果是否符合预期。
-
调用
unittest.main()
方法运行测试用例,按照函数名的排序执行测试。
以下是一个简单的例子:
python
import unittest
def login(username, password):
if username == 'kira' and password == '123':
res = {"code": 200, "msg": "登录成功"}
return res
return {"code": 400, "msg": "登录失败"}
class TestLogin(unittest.TestCase):
def test_login_success(self):
"""测试登录成功"""
test_data = {"username": "kira", "password": "test"}
expect_data = {"code": 200, "msg": "登录成功"}
res = login(**test_data)
self.assertEqual(res, expect_data)
def test_login_error_with_error_password(self):
"""账号正确,密码错误,登录失败"""
test_data = {"username": "kira", "password": "12345"}
expect_data = {"code": 400, "msg": "登录失败"}
res = login(**test_data)
self.assertEqual(res, expect_data)
# 更多测试函数类似...
if __name__ == '__main__':
unittest.main()
以上是一个简单的测试用例,包含了两个测试函数。运行脚本将输出测试结果。
unittest
核心概念
测试脚手架
测试脚手架 是测试用例的前置条件和后置条件,确保测试环境的初始化和清理,从而保证测试的准确性和可靠性。
python
import unittest
class MyTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
# 类级别的前置条件设置,整个类运行最先只执行一次
print("setUpClass")
@classmethod
def tearDownClass(cls):
# 类级别的后置条件清理,整个类运行最后结束执行一次
print("tearDownClass")
def setUp(self):
# 测试方法级别的前置条件设置,所有测试方法运行前都执行一次
print("setUp")
def tearDown(self):
# 测试方法级别的后置条件清理,所有测试方法运行结束都执行一次
print("tearDown")
def test_example(self):
# 测试用例
print("test_example")
if __name__ == "__main__":
unittest.main()
-
setUp()
:每个测试方法运行前执行,用于测试前置的初始化工作。 -
tearDown()
:每个测试方法结束后执行,用于测试后的清理工作。 -
setUpClass()
:所有的测试方法运行前执行,用于单元测试类运行前的准备工作。使用@classmethod
装饰器装饰,整个测试类运行过程中只会执行一次。 -
tearDownClass()
:所有的测试方法结束后执行,用于单元测试类运行后的清理工作。使用@classmethod
装饰器装饰,整个测试类运行过程中只会执行一次。
测试用例
测试用例 是最小的测试单元,用于检测特定的输入集合的特定的返回值。unittest
提供了 TestCase
基类,所有的测试类都需要继承该基类,而在该类下的函数如果以 test_
开头,则被标识为测试函数:
ruby
class MyTestCase(unittest.TestCase
):
def test_addition(self):
result = 2 + 3
self.assertEqual(result, 5) # 使用断言方法验证结果是否相等
def test_subtraction(self):
result = 5 - 3
self.assertTrue(result == 2) # 使用断言方法验证结果是否为True
# 更多测试用例函数...
断言方法
以下是常用的断言方法:
-
assertEqual(a, b, msg=None)
:验证 a 等于 b。 -
assertNotEqual(a, b)
:验证 a 不等于 b。 -
assertTrue(x)
:验证 x 是否为 True。 -
assertFalse(x)
:验证 x 是否为 False。 -
assertIs(a, b)
:验证 a 是否是 b。 -
assertIsNot(a, b)
:验证 a 是否不是 b。 -
assertIsNone(x)
:验证 x 是否为 None。 -
assertIsNotNone(x)
:验证 x 是否不为 None。 -
assertIn(a, b)
:验证 a 是否在 b 中。 -
assertNotIn(a, b)
:验证 a 是否不在 b 中。 -
assertIsInstance(a, b)
:验证 a 是否是 b 类型的实例。 -
assertNotIsInstance(a, b)
:验证 a 是否不是 b 类型的实例。
可以使用这些方法进行断言,也可以直接使用原生的assert来断言,如果断言失败,测试用例会被定义为执行失败。
忽略特定测试方法
unittest
提供了一些方法来跳过特定的测试用例:
-
@unittest.skip(reason)
:强制跳过,reason 是跳过的原因。 -
@unittest.skipIf(condition, reason)
:当 condition 为 True 时跳过。 -
@unittest.skipUnless(condition, reason)
:当 condition 为 False 时跳过。 -
@unittest.expectedFailure
:如果测试失败,这个测试用例不会计入失败的统计。 -
使用实例方法:
self.skipTest()
使用和上述类似。
python
import sys
import unittest
class Test1(unittest.TestCase):
@unittest.expectedFailure # 即使失败也会被计为成功的用例
def test_1(self):
assert 1 + 1 == 3
@unittest.skip('无条件跳过') # 不管什么情况都会进行跳过
def test_2(self):
print("2+2...", 4)
@unittest.skipIf(sys.platform == "win32", "跳过") # 如果系统平台为 Windows 则跳过
def test_3(self):
print("3+3...", 6)
@unittest.skipUnless(sys.platform == "win32", "跳过") # 除非系统平台为 Windows,否则跳过
def test_4(self):
print("4+4...", 8)
def test_5(self):
self.skipTest("跳过")
print("5+5...", 10)
if __name__ == "__main__":
unittest.main(verbosity=2)
测试套件
测试套件用于收集和组织多个测试用例,便于集中执行。
-
通过
unittest.main()
方法直接加载单元测试的测试模块,这是一种简单的加载方式。所有测试用例的执行顺序按照方法名的字符串表示的 ASCII 码升序排序,通过命名时使用test_01_xxx
来指定执行顺序。 -
将所有的单元测试用例
TestCase
加载到测试套件Test Suite
集合中,然后一次性加载所有测试对象。
通过 TestSuite
对象收集
此方式适用于需要自定义组合特定测试用例的情况。
scss
import unittest
class MyTestCase(unittest.TestCase):
def test_addition(self):
result = 2 + 3
self.assertEqual(result, 5)
def suite():
suite = unittest.TestSuite()
suite.addTest(MyTestCase('test_addition'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
通过 TestLoader
对象收集
TestLoader
是 unittest
框架提供的加载测试用例的类。
ini
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自动加载当前模块中所有以 'test_' 开头的测试用例函数
suite = loader.loadTestsFromModule(__name__)
runner = unittest.TextTestRunner()
runner.run(suite)
ini
import unittest
class MyTestCase(unittest.TestCase):
def test_addition(self):
result = 2 + 3
self.assertEqual(result, 5)
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自动加载 MyTestCase 类中的所有测试用例
suite = loader.loadTestsFromTestCase(MyTestCase)
runner = unittest.TextTestRunner()
runner.run(suite)
ini
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自动加载指定名称的测试用例
suite = loader.loadTestsFromName('module.MyTestCase.test_addition')
runner = unittest.TextTestRunner()
runner.run(suite)
ini
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
# 自动发现并加载指定目录中的测试用例模块
suite = loader.discover(start_dir='test_directory', pattern='test_*.py', top_level_dir=None)
runner = unittest.TextTestRunner()
runner.run(suite)
测试运行器
测试运行器是用于执行和输出测试结果的组件。常用的运行器有:
unittest.TextTestRunner
:这是unittest
框架中默认的测试运行器,会在命令行输出测试结果。通过调用run()
方法运行测试套件,并将测试结果打印到控制台。
ini
import unittest
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
runner = unittest.TextTestRunner()
result = runner.run(suite)
HTMLTestRunner
:这是一个第三方库,能够生成漂亮的 HTML 测试报告,需要进行安装。你可以通过搜索获取相关文件进行安装。
ini
import unittest
from HTMLTestRunner import HTMLTestRunner
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
with open('test_report.html', 'wb') as report_file:
runner = HTMLTestRunner(stream=report_file, title='Test Report', description='Test Results')
result = runner.run(suite)
XMLTestRunner
:这是另一个第三方库,用于生成 XML 格式的测试报告。
ini
import unittest
from xmlrunner import XMLTestRunner
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
with open('test_report.xml', 'wb') as report_file:
runner = XMLTestRunner(output=report_file)
result = runner.run(suite)
你也可以自定义测试运行器。继承 unittest.TestRunner
类并实现 run()
方法,以创建自己的测试运行器。
ini
import unittest
class MyTestRunner(unittest.TextTestRunner):
def run(self, test):
print("Running tests with MyTestRunner")
result = super().run(test)
return result
if __name__ == '__main__':
loader = unittest.defaultTestLoader
suite = loader.discover(start_dir='tests', pattern='test_*.py')
runner = MyTestRunner()
result = runner.run(suite)
通常使用 HTMLTestRunner
即可满足需求,它非常易用。
实战一个测试案例
假设有一个测试函数 login
:
python
# login.py
def login(username, password):
"""模拟登录校验"""
if username == 'kira' and password == '123456':
return {"code": 0, "msg": "登录成功"}
else:
return {"code": 1, "msg": "账号或密码不正确"}
设计用例
根据函数的参数和逻辑,设计如下用例:
序号 | 标题 | 测试数据 | 预期结果 | 实际结果 |
---|---|---|---|---|
1 | 账号密码正确 | {"username": "kira", "password": "123456"} | {"code": 0, "msg": "登录成功"} | |
2 | 账号正确密码不正确 | {"username": "kira", "password": "123"} | {"code": 1, "msg": "账号或密码不正确"} | |
3 | 账号错误密码正确 | {"username": "kir", "password": "123456"} | {"code": 1, "msg": "账号或密码不正确"} | |
编写测试用例并运行
python
import unittest
from login import login
class TestLogin(unittest.TestCase):
def test_login_correct(self):
"""测试账号密码正确"""
test_data = {"username": "kira", "password": "123456"}
expect_data = {"code": 0, "msg": "登录成功"}
res = login(**test_data)
self.assertEqual(res, expect_data)
def test_login_wrong_password(self):
"""测试账号正确密码不正确"""
test_data = {"username": "kira", "password": "123"}
expect_data = {"code": 1, "msg": "账号或密码不正确"}
res = login(**test_data)
self.assertEqual(res, expect_data)
def test_login_wrong_username(self):
"""测试账号错误密码正确"""
test_data = {"username": "kir", "password": "123456"}
expect_data = {"code": 1, "msg": "账号或密码不正确"}
res = login(**test_data)
self.assertEqual(res, expect_data)
if __name__ == '__main__':
unittest.main()
这是一个简单的测试用例,包含了三个测试函数。运行测试用例后,会输出测试结果,看完是否觉得unittest非常简单易用。ner.run(suite)
总结
以上就是勇哥今天为各位小伙伴准备的内容,如果你想了解更多关于Python自动化测试的知识和技巧,欢迎关注我:公众号\博客\CSDN\B站:测试玩家勇哥
;我会不定期地分享更多的精彩内容。感谢你的阅读和支持!
题外话,勇哥打算把新建的技术交流群,打造成一个活跃的高质量技术群。工作中遇到的技术问题,都可以在里面咨询大家,还有工作内推的机会。有兴趣的小伙伴,欢迎加我(记得备注是进群还是报名学习)
勇哥,10年落魄测试老司机,技术栈偏python,目前在一家超大型房产公司担任自动化测试主管,日常工作比较繁杂,主要负责自动化测试,性能测试、软件质量管理及人员管理。工作之余专注于为粉丝进行简历修改、面试辅导、模拟面试、资料分享、一对一自动化测试教学辅导等副业发展。目前已服务十多位小伙伴,取得高薪offer。
关注公众号,测试干货及时送达
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 接口自动化测试项目2.0,让你像Postman一样编写测试用例,支持多环境切换、多业务依赖、数据库断言等 |
| 揭秘抓包利器:Python和Mitmproxy让您轻松实现接口请求抓取与分析! |
| 构建高效的接口自动化测试框架思路 |
| Pytest 快速入门 |
| 接口自动化之测试数据动态生成并替换 |
| requests模块该如何封装? |
| 接口自动化如何封装mysql操作 |
| 一文看懂python如何执行cmd命令 |
| 最通俗易懂python操作数据库 |
| python-Threading多线程之线程锁 |
| python正则一篇搞掂 |
| 性能测试之必备知识 |
| 性能分析思路 |
| Python + ChatGPT来实现一个智能对话的钉钉机器人 |
| 一文看懂python如何执行cmd命令 |
[往期精选文章:]