Appearance
19命令行参数,助力测试框架高度定制化
欢迎进入"模块四 深入自动化测试框架原理",算上开篇词、课前必读,以及前三个模块的学习,你对测试框架应该有了非常深刻的认识。
按照本课程知识,现在你已经能融合 API 和 UI 搭建出功能丰富的测试框架,并可以使用 PageObject 模型分离元素和操作;之后,我们又让测试框架具备 Data Driven 能力,并搭配 Jira 或者禅道创建出具备测试数据管理能力的一揽子测试解决方案。
用这些技能应对日常测试工作肯定没有任何问题,但是每一个有追求的测试人都会想:我能不能自己造一个轮子?
小提问: 为什么编写自己的测试框架或者应用程序,被称为造轮子?请带着这个问题进行下面的学习,在后面的部分我会解答。
从本讲开始,我将拆解自动化测试框架的重要组成部分,并带领你一一自主实现。模块四结束时,你应该就具备不借助任何第三方库,独自开发测试框架的能力了。
今天我们先来看测试框架的第一个重点部分:使用命令行参数定制化测试框架,下图是本讲的内容结构,可供你学习参考。
在 《10 | 你的第一个 API 测试框架(二)》 中,我讲了如何使用命令行参数来运行 pytest 命令。在 pytest 框架中,预置的命令行参数起着非常重要的作用,比如我们可以利用"-k"来根据函数名称挑选测试用例,利用"-m"来分组挑选测试用例。
但有些时候,我们还是希望可以定制一下命令行参数。比如,我的测试数据想通过命令行的方式传递,下面我们就来实际操作下。
pytest 添加命令行参数
假设我们的文件结构如下:
python
|--lagouAPITest
|--tests_command_lines
|--conftest.py
|--test_sample.py
|--__init__.py
其中 conftest.py 的代码如下:
python
import pytest
def pytest_addoption(parser):
parser.addoption(
"--auth", action="store", default=None, help="Your own auth key pair"
)
@pytest.fixture(scope='session')
def auth(request):
return request.config.getoption('--auth')
我在前面的章节讲过,在 conftest.py 定义的 fixtures 可作用于整个 Package 下的多个测试文件。在此 conftest.py 里,我定义了一个函数 pytest_addoption,这里注意:
pytest_addoption 是一个 hook 方法,其名称不可改变;
pytest_addoption 允许用户注册一个自定义的命令行参数,方便用户使用命令行传递数据;
pytest_addoption 仅能在 conftest.py 文件或者 pytest plugins 里实现;
pytest_addoption 在测试用例执行前被调用。
然后我们结合前面章节讲过的 fixture 函数,定义一个名字为 auth 的 fixture 接收自定义的命令行参数,并提供给整个 package 下的测试用例使用,这里需要注意的是:
auth 函数是正常的 fixture 函数,需要加装饰器 pytest.fixture 装饰;
auth 函数的 scope 可以自由定义,这里我定义为 session;
auth 函数的命名可以更改,但是参数 request 不可更改;
request.config.getoption 用于接收命令行命令参数。
通过函数 pytest_addoption 以及定义 fixture 方法,我们就可以自由定义命令行参数。
我们再来看下 test_sample.py 文件的代码:
python
import pytest
class TestDemo:
def test_secret_auth(self, auth):
print("\nmy auth are {}".format(auth))
assert True
这段代码非常简单,定义了一个测试函数 test_secret_auth,其参数是之前定义的 fixture 函数auth。
在命令行中通过如下方式运行:
python
pytest tests_command_lines -k "secret_auth" -s -v --auth iTesting
运行结束后,可以发现我们传递的自定义命令行参数 auth 的值被正确接收并显示:
python
# 部分运行输出
collected 1 item tests_command_lines/test_sample.py::TestDemo::test_secret_auth
my auth are iTesting
PASSED
通过自定义命令行参数,可以扩展测试框架功能。例如可以通过命令行参数实现,当我从命令行传递某个具体的指令时,skip 掉一些测试用例。
如果你仔细观察上文中pytest_addoption 这个函数,你会发现它定义命令行参数使用了 parser.addoption。那么这个parser.addoption是什么呢?
下图是 parser.addoption 的源代码:
通过代码可以发现,addoption 接收如下参数。
*optnames:它是一个可变字符串,用来表示 option 的名称,例如上面例子中的"--auth";
attrs:attrs 是关键字参数,通常以 key:value 的形式存在,它能接收的参数与标准库 argparse**的 add_argument() 这个函数可接收的参数一致。
自主定义命令行参数(argparse 库)
如果你进一步查看 pytest 的源码,你将发现其实 pytest 中用于解析命令行参数的 parser 正是argparse 这个标准库。
argparse 是内置于标准库中的,用于 python 命令行解析的模块,使用 argparse 可以直接在命令行中向程序传入参数。argparse 用法,一般遵循如下步骤。
1. 导入 argparse 模块
argparse 无须安装,使用时直接 import 即可:
python
import argparse
2. 创建 ArgumentParser 对象
html
parser = argparse.ArgumentParser()
下面列出 ArgumentParser 可接收的常用参数如下:
python
prog - 程序的名称(默认:sys.argv[0])
usage - 描述程序用途的字符串(默认值:从添加到解析器的参数生成)
description - 在参数帮助文档之前显示的文本(默认值:无)
epilog - 在参数帮助文档之后显示的文本(默认值:无)
parents - 一个 ArgumentParser 对象的列表,它们的参数也应包含在内
formatter_class - 用于自定义帮助文档输出格式的类
prefix_chars - 可选参数的前缀字符集合(默认值:'-')
fromfile_prefix_chars - 当需要从文件中读取其他参数时,用于标识文件名的前缀字符集合(默认值:None)
argument_default - 参数的全局默认值(默认值: None)
conflict_handler - 解决冲突选项的策略(通常是不必要的)
add_help - 为解析器添加一个 -h/--help 选项(默认值: True)
allow_abbrev - 如果缩写是无歧义的,则允许缩写长选项 (默认值:True)
exit_on_error - 决定当错误发生时是否让 ArgumentParser 附带错误信息退出。 (默认值: True)
一般情况下,prog、usage、description 这三个参数使用得比较多。
3. 添加参数
python
# 可以通过add_argument添加一个或多个参数
parser.add_argument()
add_argument() 方法用于指定程序能够接受哪些命令行参数,add_argument() 接收两种类型的参数,分别是位置参数 (Positional arguments) 和可选参数 (Optional arguments)。下面详细介绍下两种参数。
- 位置参数(Positional arguments)
位置参数是指必须传递的参数,如果不传递就会报错。例如,在 tests_command_lines 文件夹下创建一个文件:
python
# test_argparse.py
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("name", help="This is a demo",action="store")
args = parser.parse_args()
if args.name:
print(args.name)
首先,在命令行中查看支持的参数:
python
# 定位到test_argparse.py所在的文件夹,然后直接执行
D:\_Automation\lagouAPITest\tests_command_lines>python test_argparse.py -h
你会看到如下输出:
python
D:\_Automation\lagouAPITest\tests\tests_command_lines>python test_argparse.py -h
usage: test_argparse.py [-h] name
positional arguments:
name This is a demo
optional arguments:
-h, --help show this help message and exit
通过 -h 命令,可以查看定义的所有位置参数(本例中是 name)。
下面不给定位置参数直接运行:
python
D:\_Automation\lagouAPITest\tests\tests_command_lines>python test_argparse.py
运行结果如下:
python
usage: test_argparse.py [-h] name
test_argparse.py: error: the following arguments are required: name
由此可见,如果定义了位置参数,则位置参数必须传递。
如果给定参数的值运行:
python
D:\_Automation\lagouAPITest\tests\tests_command_lines>python test_argparse.py iTesting
则会运行成功。
- 可选参数(Optional arguments)
相对于位置参数,可选参数的定义如下:
python
# test_argparse.py
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--name", default='iTesting', help="This is a demo",action="store")
args = parser.parse_args()
if args.name:
print(args.name)
不给定可选参数直接运行:
python
D:\_Automation\lagouAPITest\tests\tests_command_lines>python test_argparse.py
运行结果如下:
python
D:\_Automation\lagouAPITest\tests\tests_command_lines>python test_argparse.py
iTesting
当可选参数不填写时,运行也不会报错。如果可选参数有默认值,argparse 将会把此可选参数默认传入。
4.解析参数
解析参数即"步骤3 添加参数"中的对参数的解析:
python
args = parser.parse_args()
ArgumentParser 通过 parse_args() 方法,把每个参数转换为适当的类型然后调用相应的操作。例如"**步骤3 添加参数"**中的打印语句:
python
if args.name:
print(args.name)
了解了 argparse 的基本使用后,我们就可以通过命令行参数来更好的执行测试。更多关于 argparse 的高级用法,请直接参考 Python 标准库。
测试框架雏形 ------ 创建自己的命令行程序
现在我们知道,有了 argparse 就可以通过命令行参数来进行测试,那么假设你有一个测试框架,如何让你的测试框架也支持通过命令行测试呢?或者说,你有没有好奇过,为什么我们在命令行中直接输入"pytest", 测试脚本就会运行?
下面,我们就通过创建一个支持命令行参数的"测试框架"来解开这个疑惑。
1. 创建项目
首先,创建一个项目,名称为 iTestingDemoFramework。
python
|--iTestingDemoFramework
|--iTesting
|--main.py
|--__init__.py
|--setup.py
其次,更新各个文件的代码。其中 main.py 的内容如下:
python
# main.py
import argparse
def main():
parser = argparse.ArgumentParser(prog='iTesting', usage='This is a demo, please follow iTesting on wechat')
parser.add_argument("name", default='iTesting', help="This is a demo framework", action="store")
args = parser.parse_args()
if args.name:
print("Hello, My name is Kevin Cai, Please search and follow below account from wechat:\n")
print(args.name)
if __name__ == "__main__":
main()
setup.py 的代码如下:
python
from setuptools import setup, find_packages
setup(
name='iTesting',
version='0.1',
description='This is a demo framework',
author='kevin.cai',
author_email='testertalk@outlook.com',
zip_safe=False,
include_package_data=True,
packages=find_packages(),
license='MIT',
url='https://www.helloqa.com',
entry_points={
'console_scripts': [
'iTesting = iTesting.main:main'
]
}
)
setup.py 文件是 python 模块安装所需要的文件,它描述了你的模块的各项信息。你可以按照上面的代码,创建自己的 setup.py 文件。更多关于 setup.py 的知识,请查看 setuptools。
2. 检验 setup.py
在命令行中切换到项目所在的根目录,执行如下命令:
python
# 切换到项目根目录, 此处是iTestingDemoFramework
python setup.py check
这个命令会验证 setup.py 里所填写内容的正确性及安装相应的依赖包(如果有的话)。如果 setup.py 的内容及格式不正确,则命令行中会提示。
3. 本地验证
当执行检验 setup.py,没有错误提示后,我们就可以在本地验证包的内容:
python
# 1.项目根目录下,此处为iTestingDemoFramework。执行
python setup.py install
# 2.执行成功后,在命令行执行如下命令:
iTesting -h
执行后,可以看到 Console 的输出如下:
html
(venv) D:\_Automation\iTestingDemoFramework>iTesting -h
usage: This is a demo, please follow iTesting on wechat
positional arguments:
name This is a demo framework
optional arguments:
-h, --help show this help message and exit
注意:此刻执行命令时不必在项目根目录下,你可以打开命令行直接输入 iTesting 也是可以执行的,因为 iTesting 这个程序已经被我们注册到系统了。
继续测试,打开命令行,直接输入我们定义的命令行参数 name 的值:
python
iTesting iTesting
执行完毕后输出如下:
python
Hello, My name is Kevin Cai, Please search and follow below account from wechat:
iTesting
由此可以看到,命令行程序创建成功。如果 iTesting 是你的测试框架,是不是就意味着你的测试框架可以直接通过命令行运行了。
其实这也是为什么你在命令行中直接执行 pytest,pytest 会运行的原因。
当然 pytest 里还有查找测试用例的逻辑,但是我们的程序 iTesting 里没有实现。
测试框架雏形 ------上传自己的命令行程序
当你的测试框架开发完成,并且支持命令行执行后,你就可以上传至 Pypi,分享给更多人使用。
PyPI(Python Package Index) 是 python 官方的第三方库的仓库,可以帮助我们查找和安装 Python 社区开发和共享的软件。
下面演示下如何将你的应用程序打包(以 Win 10 系统为例)。
1. 注册 PyPI 账户
要上传你自己打包好的程序到 PyPI,必须先注册一个账户,登录PyPI 官网直接注册即可。
2. 打包程序
你的程序要上传到 PyPI,必须打包。在命令行中执行如下命令,将程序打包:
python
# 切换到项目根目录, 此处是iTestingDemoFramework
python setup.py sdist build
打包成功后,你会看到项目根目录下多了三个文件夹,分别是 build、dist 以及 iTesting.egg-info,如下图所示:
打包的方式有很多种,除了上述所讲外,还可以用 wheel 打包。
首先安装 wheel:
java
pip install wheel
安装好后,就可以把应用程序打包:
java
python setup.py bdist_wheel
wheel 翻译过来就是"轮子"的意思,所以创建自己的程序并打包成 wheel,就是一个造轮子的过程。后来造轮子,专指编写与已有应用程序拥有相同功能的程序。
3. 发布程序
首先,应该确保 twine 这个库安装。
Twine 是一个在 PyPI 上发布 Python 包的实用程序。
python
pip install twine
其次,通过命令行工具,在你的项目根目录下,通过如下命令上传:
python
# 在项目根目录下执行,本例为 iTestingDemoFramework
D:\_Automation\iTestingDemoFramework>twine upload dist/*
执行后,系统会交互式询问,让你提供 PyPI 上注册的用户名和密码,填写正确即可上传成功:
最后访问上传后的地址,你可以看到,我们创建的程序已经被正确上传。
4. 通过 pip 安装使用
应用程序上传至 PyPI 后,每个人都可以通过 pip install 直接下载使用,在命令行中输入:
java
pip install iTesting
注意,此命令可在任意目录下执行,不必限制在我们项目根目录下,因为我们的程序已经上传至 PyPI。
你会看到如下安装界面:
安装成功后,我们来测试下我们的功能, 在命令行中直接输入:
java
iTesting iTesting
可以看到,结果如下:
java
Hello, My name is Kevin Cai, Please search and follow below account from wechat:
iTesting
至此,你的"测试框架"已经上架并可被任何人下载使用。
小结
我来总结下今天所讲的内容。
本章从 pytest 命令行参数引入,首先回顾了下 pytest 内置的命令行参数使用(例如 -k 的使用);接着,在 pytest 框架中,创建自己的自定义参数来扩展应用程序功能。
然后我们更进一步,通过阅读 pytest 源码,得知 pytest 解析命令行参数正是用的 argparse 这个标准库,于是我介绍了 argparse 的基本使用,并带领大家实现了一个基于命令行的测试框架雏形(虽然什么具体功能都没有),并结合打包程序上传至 PyPI。
通过这个过程,你应该对如何开发支持命令行参数的测试框架,如何发布测试框架供他人使用这个流程有了比较清楚的了解。
好的,在后面的章节中,我将继续带领你,深入测试框架原理。
我是蔡超,我们下节课再见。
更多关于"测试框架"的知识,请关注公众号 iTesting 并回复"测试框架"查看。