ISEE小语
“狮子从不在意绵羊的看法。”
——《权力的游戏》
本次我们用到了unittest,它是Python标准库中的单元测试框架。它提供了一个强大且灵活的测试工具,旨在帮助开发者编写和运行可重复的自动化测试。
其实测试并不单单只在测试人员介入的情况下进行,在研发人员开发的过程中或研发自测的时候,就已经针对实现逻辑开始了测试。
我们这次实现一个接口测试和报告输出的实例,测试报告使用的是三方库BeautifulReport,简单且效果亮眼。
环境:
Pycharm
Python 3.9.16
安装:
本次我们用到的三方库,用以下方式可自行安装:
pip install BeautifulReport==0.1.3
pip install requests==2.32.3
pip install PyYAML==6.0.1
先看实际效果
项目结构
首先,我们看一下 自动化测试 小项目的结构:
```结构
auto_test
├─data
│ └─words.yaml
├─reports
├─testcases
│ ├─test_words_add_01.py
│ └─test_words_add.py
├─main.py
└─requirements.txt
```
实现操作
主要实现步骤:
单用例调试:就是用固定的数据测试接口功能是否正常
用例批量测试:使用多数据进行测试
用例参数化:对测试用例数据进行参数化管理
生成测试报告:最终输出一个界面化的测试报告
单用例调试
首先,在testcases目录下,新建单用例测试文件:
【test_words_add_01.py】,编写测试用例脚本
# -*- coding: utf-8 -*-
import json
import requests
import unittest
class Test_Words(unittest.TestCase):
def setUpClass(cls) -> None:
print("============开始测试==========")
def tearDownClass(cls) -> None:
print("============测试完成!==========")
def test_01_add(self):
"""测试添加词汇:“清晨”"""
headers = {"Content-Type": "application/json"}
req_data = {"word": "清晨", "status": 1}
resp_result = requests.post(url='http://127.0.0.1:5000/add', json=req_data, headers=headers)
result_code = 200
result = json.loads(resp_result.text).get('result')
expe_result = 0
self.assertEqual(result_code, resp_result.status_code, "请求失败")
self.assertEqual(result, expe_result, "请求失败")
if __name__ == '__main__':
unittest.main()
(左右滑动查看完整代码)
注意:
其中使用的被测项目是在本地启动的一个简化版的词汇录入项目服务,名为【词海】,以下实例都使用这个服务做演示
http://127.0.0.1:5000/add
这个服务很简单,用Flask启动的,但也会在文章后面一同共享出来。
OK,那我们现在直接运行 test_words_add_01.py
这个结果说明调试成功,看一下数据库中,
用例批量测试
在testcases目录下,新建多用例测试文件:
【test_words_add.py】,
用例批量测试,无非就是多数据一起进行测试,在编写测试脚本的时候,有两种方式:
第一种是比较僵硬,就是在 单用例 的基础上,逐个添加测试用例。
如下:
# -*- coding: utf-8 -*-
import json
import requests
import unittest
class Test_Words(unittest.TestCase):
def setUpClass(cls) -> None:
print("============开始测试==========")
def tearDownClass(cls) -> None:
print("============测试完成!==========")
def test_01_add(self):
"""测试添加词汇:“清晨”"""
headers = {"Content-Type": "application/json"}
req_data = {"word": "清晨", "status": 1}
resp_result = requests.post(url='http://127.0.0.1:5000/add', json=req_data, headers=headers)
result_code = 200
result = json.loads(resp_result.text).get('result')
expe_result = 0
self.assertEqual(result_code, resp_result.status_code, "请求失败")
self.assertEqual(result, expe_result, "请求失败")
def test_02_add(self):
"""测试添加词汇:“花开”"""
headers = {"Content-Type": "application/json"}
req_data = {"word": "花开", "status": 1}
resp_result = requests.post(url='http://127.0.0.1:5000/add', json=req_data, headers=headers)
result_code = 200
result = json.loads(resp_result.text).get('result')
expe_result = 0
self.assertEqual(result_code, resp_result.status_code, "请求失败")
self.assertEqual(result, expe_result, "请求失败")
def test_03_add(self):
"""测试添加词汇:“月光”"""
headers = {"Content-Type": "application/json"}
req_data = {"word": "月光", "status": 1}
resp_result = requests.post(url='http://127.0.0.1:5000/add', json=req_data, headers=headers)
result_code = 200
result = json.loads(resp_result.text).get('result')
expe_result = 0
self.assertEqual(result_code, resp_result.status_code, "请求失败")
self.assertEqual(result, expe_result, "请求失败")
if __name__ == '__main__':
unittest.main()
(左右滑动查看完整代码)
可以看到本次的用例是 ['清晨', '花开', '月光'],
用例方法从test_01_add 添加到 test_03_add
结果:
从 Ran 3 tests in 3.083s 可以看出,执行成功3个用例,看一下数据库中
OK,这个结果是没有问题的,但设计中有个问题是,如果用例数据比较多的情况下,就得手动一直增加用例方法,会很繁琐。
接下来,我们为了解决这个问题,需要根据数据动态创建测试用例方法,看一下第二种方式,如下:
# -*- coding: utf-8 -*-
import json
import requests
import unittest
class Test_Words(unittest.TestCase):
def setUpClass(cls) -> None:
print("============开始测试==========")
def tearDownClass(cls) -> None:
print("============测试完成!==========")
words = ['清晨', '花开', '月光']
# 动态创建测试用例方法
def create_test(word):
def test(self):
headers = {"Content-Type": "application/json"}
req_data = {"word": word, "status": 1}
resp_result = requests.post(url='http://127.0.0.1:5000/add', json=req_data, headers=headers)
result_code = 200
result = json.loads(resp_result.text).get('result')
expe_result = 0
self.assertEqual(result_code, resp_result.status_code, f"请求失败,词汇: {word}")
self.assertEqual(result, expe_result, f"请求失败,词汇: {word}")
# 添加测试用例描述
test.__doc__ = f"测试添加词汇:'{word}'"
return test
# 动态添加测试方法到 Test_Words 类
for word in words:
test_name = f'test_add_{word}'
test_method = create_test(word)
setattr(Test_Words, test_name, test_method)
if __name__ == '__main__':
unittest.main()
(左右滑动查看完整代码)
结果:
从运行结果可以看出,执行成功3个用例,看一下数据库中
OK,这样设计,只需要变动测试数据即可
words = ['清晨', '花开', '月光']
用例参数化
用例数据参数化,是自动化测试中常用的一个方式,目的是方便维护测试用例
存放测试用例的方式大概有Excel、csv、txt文本、yaml文件和数据库等,本次我们将用例数据放在一个yaml文件中。
对于yaml文件的使用,在以前分享的文章中有记录,有兴趣可以看一下,这里就直接使用了。
Python对yaml文件的增删改查操作
Isee小栈,公众号:ISEE小栈Python对yaml文件的增删改查操作
首先,在data目录下创建words.yaml文件,格式如下:
words:
- 清晨
- 花开
- 月光
- 星辰
- 秋水
- 眉间
- 流年
- 心语
- 芳华
- 静谧
- 锦绣
- 岁月
实际放了50个词汇数据
参数化后的完整代码如下:
# -*- coding: utf-8 -*-
import json
import requests
import unittest
import yaml
class Test_Words(unittest.TestCase):
def setUpClass(cls) -> None:
print("============开始测试==========")
def tearDownClass(cls) -> None:
print("============测试完成!==========")
with open('../data/words.yaml', 'r', encoding='utf-8') as file:
try:
data = yaml.safe_load(file)
words = data['words']
except yaml.YAMLError as exc:
print(exc)
# 动态创建测试用例方法
def create_test(word):
def test(self):
headers = {"Content-Type": "application/json"}
req_data = {"word": word, "status": 1}
resp_result = requests.post(url='http://127.0.0.1:5000/add', json=req_data, headers=headers)
result_code = 200
result = json.loads(resp_result.text).get('result')
expe_result = 0
self.assertEqual(result_code, resp_result.status_code, f"请求失败,词汇: {word}")
self.assertEqual(result, expe_result, f"请求失败,词汇: {word}")
# 添加测试用例描述
test.__doc__ = f"测试添加词汇:'{word}'"
return test
# 动态添加测试方法到 Test_Words 类
for word in words:
test_name = f'test_add_{word}'
test_method = create_test(word)
setattr(Test_Words, test_name, test_method)
if __name__ == '__main__':
unittest.main()
(左右滑动查看完整代码)
为了看到成功和失败的结果,词汇录入项目在50个词汇中,已经存在'清晨', '花开', '月光' 这三个词汇。
测试场景为:如果词汇已经存在,则不录入,返回result为1的失败结果
OK,执行以上测试
测试结果正确!
生成测试报告
生成测试报告,使用的是BeautifulReport,它是一个用于生成测试报告的 Python三方库,特别是与 unittest 框架一起使用。它可以生成美观的 HTML 测试报告,显示测试结果的详细信息,包括测试通过、失败、错误等情况。
在main.py主执行文件中
# -*- coding: utf-8 -*-
import time
import unittest
from BeautifulReport import BeautifulReport as bf
def report_out(test_dir, report_dir, project, theme):
"""
:test_dir: 测试用例路径
:report_dir : 测试报告路径
:project : 项目名称
:theme: 报告主题名(theme_default:默认; theme_cyan:青色; theme_candy:糖果绿; theme_memories:记忆蓝
:return: 无
"""
now = time.strftime("%Y_%m_%d %H_%M_%S")
# 加载测试用例
test_cases = unittest.defaultTestLoader.discover(test_dir, pattern='test_words_add_01.py')
report_name = now + '-' + project + '_test_report.html' # 报告名称
# 生成HTML测试报告
run = bf(test_cases)
run_result = run.report(filename=report_name, report_dir=report_dir, description="词海测试", theme=theme)
return run_result # 返回测试用例
if __name__ == '__main__':
# 生成HTML报告并获取测试用例
test_case = report_out('testcases', 'reports', '词海', 'theme_cyan')
(左右滑动查看完整代码)
执行测试
在电脑上启动,直接切换到venv下,输入:
python main.py
出现以上日志信息说明自动化测试执行成功!
接下来……
打开在浏览器中打开输出的.html报告
常用方法和场景
断言方法:
assertEqual(a, b):验证 a == b
assertNotEqual(a, b):验证 a != b
assertTrue(x):验证 x 为 True
assertFalse(x):验证 x 为 False
assertIs(a, b):验证 a is b
assertIsNot(a, b):验证 a is not b
assertIsNone(x):验证 x is None
assertIsNotNone(x):验证 x is not None
assertIn(a, b):验证 a in b
assertNotIn(a, b):验证 a not in b
assertRaises(exc, fun, *args, **kwds):验证在调用 fun 时抛出指定的异常 exc
使用场景:
单元测试:验证单个模块或函数的正确性,确保其在各种输入条件下都能返回预期的结果。
回归测试:在代码修改后,运行一组测试用例,确保新代码没有引入新的错误。
集成测试:测试多个模块或组件的集成,验证它们能够正确地协同工作。
持续集成:在 CI/CD 流水线中,自动运行测试用例,确保每次代码提交后软件的稳定性。
总结
小栈将被测项目【词海】和 自动化测试代码实例整体梳理了,有兴趣可下载。
本次分享可能会有些BUG,但整体流程是OK的,这个测试是一个从始到终的闭环,自己单电脑就可以完成学习,有问题欢迎留言。
项目源码整理:
项目启动,安装好env虚拟环境和Mysql后,将sqls导入Mysql,最后直接运行app.py即可。
前期也分享过,虽然功能不一样,但启动方式一样,有需要可以了解
Flask+pyecharts+SQLAlchemy,统计图的数据存放在mysql中
ISEE小栈,公众号:ISEE小栈Flask+pyecharts+SQLAlchemy,统计图的数据存放在mysql中,综合版
测试源码整理:
直接执行main.py即可!
相遇即是缘
朋友点个“赞”和“在看”,谢谢支持~!
后台回复“unittest”即可获取!
文章就分享到这儿,喜欢就点个赞吧!