自动化工具 测试开发之路 (工具篇)----pytest and pytest-allure-adaptor

孙高飞 · February 14, 2017 · Last by 江楷 replied at December 13, 2018 · 9667 hits

背景

是的, 我这个java技术栈的测试人开始玩python了,公司产品向用户提供了python的sdk,所以为了能针对sdk做一些东西。我这两天研究了一下python和python通用的测试框架。一开始在社区和群里问了一些小伙伴们使用的都是什么,发现大多数人用的都是unittest+HTMLTestRunner。 可能是我被java界那庞大的测试生态圈惯坏了,第一天就已然受不了unittest+ HTMLTestRunner的这种组合。所谓工欲善其事必先利其器,我花了一点时间google了一下python界常用的一些测试框架,像unittest2,nose,pyunit,doctest什么的。 挨个去官网查看了一下使用方式。发现pytest还是比较符合我的期望的。而且我用java的时候就一直使用的report框架----allure 也支持了pytest。 所以今天还是老规矩。入门科普,抛砖引玉,详尽的文档请参考:http://doc.pytest.org/en/latest/contents.html 以及: https://github.com/allure-framework/allure-pytest

执行方式

pytest的执行方式非常简单, 它跟xunit 系的框架不一样,测试类不需要继承任何pytest的类。 在运行pytest 命令的时候,自然会有一套case discover的机制识别。只需要你的类是以也就是说你的测试类和方法可以跟普通方法是一样的。只需要你的测试文件的命名规则符合test_*.py 或者 *_test.py。 例子如下:

class TestDataLoad():
def test_data_load(self, me):
source_table = None
# 判断是否已经导入这个数据
bank_data = [t for t in me.tables if t.name == 'test_gaofei01']
if len(bank_data) != 0:
source_table = bank_data.pop()
else:
source_table = me.new_local_table('./banking_2_23.csv', 'test_gaofei01', format_='csv',
first_line_schema=False)

这个时候我们到目录下执行py.test。你会发现已经自动发现了你的测试函数并执行。 不需要像其他框架一样继承某一个类或者在函数上使用某个装饰器。 关于用例运行的具体配置,可以参照链接:http://doc.pytest.org/en/latest/usage.html

Test Fixture

fixture 一词最早是在xunit test patterns 看到的。我也不知道翻译过来到底该叫什么,可以认为是测试所依赖的所有东西,例如参数,测试数据,数据库连接等。 在其他测试框架中一般提供了@setup @teardown 这类的方法。python中使用@pytest.fixture 这样一个装饰器。我们直接看一个最简单的例子。

class ProphetClientBase(object):
@pytest.fixture(scope='session')
@allure.step('登录')
def me(self):
client = ProphetClient('http://172.27.1.115:8888')
assert client.login('Admin', '1234567'), '登录失败'
return client.me

上面我们用了pytest提供的装饰器来创建我们的fixture。 这个方法也很简单,主要负责登录并返回用户对象。 这个用户对象是大部分case需要用的。 所以我们把它定义为一个fixture。在其他框架中我们用setup method的方式来做的,这样做有个缺点就是setup method无法传递数据给测试方法作为参数。但是pytest中我们可以像下面一样做:

def test_data_load(self, me):
source_table = None

# 判断是否已经导入这个数据
bank_data = [t for t in me.tables if t.name == 'test_gaofei01']

上面我们定义测试方法的时候直接在参数中使用me这个参数。pytest在运行的时候会自动的帮我们找到me这个fixture运行并将返回值赋值到me中。不用我们做其他的处理。 同样我们在fixture的定义中可以看到我们使用了scope='session', 意思是这个fixture是属于session级别的,在一个session中只会运行一次。是不是有点像testng的beforeTest系列了~

after系列

刚才说了before系列,现在我们说说after系列,也即是teardown。 一个fixture有时候是需要进行销毁操作的。 其他框架使用after或者teardown方法。 但是pytest可以直接在fixture中定义这个操作。只需要使用python的关键字yield 代替return。 并在yield之后编写销毁操作。 举个例子,还是上面的登录的fixture,现在我们这么写:

@pytest.fixture(scope='session')
@allure.step('登录')
def me(self):
client = ProphetClient('http://172.27.1.115:8888')
assert client.login('Admin', '1234567'), '登录失败'
yield client.me
client.me.logout()

上面我们用yield 代替return后执行了logout操作。 这样就好像是生成器一般在所有使用这个fixture的测试执行结束后。运行了logout操作。同样pytest 也支持with 。。 as大法来配合yield。如下:

@pytest.fixture
def passwd():
with open("/etc/passwd") as f:
yield f.readlines()

def test_has_lines(passwd):
assert len(passwd) >= 1
自省功能

额,我是直接按字面直接翻译了。 其实这个功能就像是java的反射一样,可以在fixture中动态获取测试模块的属性来动态的执行。直接给个例子, 我们在一个文件中定义fixture

@pytest.fixture(scope="module")
def smtp(request):
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
smtp = smtplib.SMTP(server)
yield smtp
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()

然后再另一个文件中定义测试模块

smtpserver = "mail.python.org"  # will be read by smtp fixture

def test_showhelo(smtp):
assert 0, smtp.helo()

在上面的例子中我们在fixture中使用了request这个pytest提供的默认参数,它其中有一个功能是可以提供测试方法的运行环境(我理解这个功能很像java 反射的功能之一)。在fixture中我们通过getattr直接反射出了测试模块中一个叫smtpserver的属性。并使用这个属性启动server。

参数化fixture

我们可以参数化fixture并且让测试函数根据不同的fixture运行多次测试(很像是数据驱动的一种实现)。例子如下:

@pytest.fixture(scope="module",
params=["smtp.gmail.com", "mail.python.org"])
def smtp(request):
smtp = smtplib.SMTP(request.param)
yield smtp
print ("finalizing %s" % smtp)
smtp.close()

很简单,直接使用params传入一个参数列表。然后再fixture中使用request.param使用当前的参数。 这样你会发现测试方法会使用这两个fixture执行两次。

使用方式

除了在测试方法中显示的使用fixture函数作为参数外,我们还可以让fixture传递参数而自动执行。只需要使用@pytest.mark.usefixtures("cleandir") 这个方式就可以了。

@pytest.mark.usefixtures("cleandir")
class TestDirectoryInit:
def test_cwd_starts_empty(self):
assert os.listdir(os.getcwd()) == []
with open("myfile", "w") as f:
f.write("hello")
def test_cwd_again_starts_empty(self):
assert os.listdir(os.getcwd()) == []

上面的例子里在class的定义中使用这个装饰器。这样在运行这个class的测试方法前都会执行一次fixture方法。 好了关于fixture的主要就介绍这么多了。 详细的请看链接:http://doc.pytest.org/en/latest/fixture.html

测试方法的参数化

刚才说了fixture的使用方式,但是还是无法满足我们的日常需要,我们希望有更灵活的方式给测试方法进行参数化。例如我们日常的数据驱动的测试策略。 pytest中同样给我们提供了这样的一个方式。看下面的例子:

import pytest
def value(xmlPath):
# 读取参数文件的代码
pa = [
("3+5", 8),
("2+4", 6),
("6*9", 54),
]
return pa
@pytest.mark.parametrize("test_input,expected", value(path))
def test_eval(test_input, expected):
print(test_input)
assert eval(test_input) == expected

上面是我为了实现我在testng中做数据驱动的功能而写的demo。通过parametrize这个装饰器我们就实现了跟testng中的data provider很相似的功能。tesng是接受一个2维数组,而pytest则是一个装有多个元祖的列表。这个我觉得可能不用多说了,知道数据驱动的同学都懂。

好了关于pytest我就先介绍到这吧,pytest是个很大的框架但是使用起来很简单。我今天只介绍了我们常用的比较重要的功能。 详细的文档请参阅:http://doc.pytest.org/en/latest/contents.html

allure-pytest

下面来看看为了显示高大上的report,我们抛弃了HTMLTestRunner而直接使用allure-pytest。 我之前发过帖子专门说明了allure在java和jenkins上如何配置插件,详情请看:https://testerhome.com/topics/5738。 来我先安利一下效果图:

内置日志系统,展示清楚,分类方便,可以上传附件。 总之我太喜欢这个report框架了。使用方式也很简单,安装pytest-allure-adaptor这个模块后,执行测试的时候增加一个参数就好了。如下:

py.test --alluredir report

这样我们的测试报告就生成在report目录下了。跟java的标注不太一样,pytest使用装饰器的样子大概是这样的。

step这个装饰器就是我们页面上看到的日志功能,每调用一次这个方法,页面上就会显示这个日志。具体用法看我的帖子和allure的官方文档吧。

总结

好了,今天先说这么多,我初入python圈以后还忘大家多多指教

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 52 条回复 时间 点赞

赞!!我目前在搭建的Python断言框架用的也是pytest,很好用!推荐的allure-pytest这个不错,正缺报告类的,打算后面也要集成进来。

官方例子也是用pytest。。想想当年自学时手写装饰器真是太傻逼了😂

赞!!

😊😊咱测试就是要这种精神

—— 来自TesterHome官方 安卓客户端

使用 pytest + allure 的時候需要注意一下版本,現在的版本只支援 pytest>=2.7.3,<=2.9.0,如果使用 pytest 2.9.0 以上的話,可以看一下別人提交的 PR。不知道為什麼官方死活不 Merge

#5楼 @esuter 是的,已经有人提交了fix了。 就在前几天我还在github上看到了这个request,就是最近几天提交的。 只是现在还没有提交进去。 我觉得就快了吧。我看了提交的代码,一共也没几行。 没道理要拖很久

#2楼 @dadeshuo 我刚用java那会也一样,自己造了一堆轮子。。😂 😂 😂 淡定,就当学习了

😳 初学python就开始涉猎装饰器。。。。这已经是一步跨百步了

#8楼 @jamesparagon 毕竟搞了几年java了~~ 实现的目的相同~ 只是语法不一样了么

赞一个,准备使用这一套.现在用nose+ HTMLTestRunner不舒服

默默的赞一个

@ycwdaaaa 我安装好了pytest 和pytest-allure-adaptor
import pytest
import allure
都是成功的,但是就是不能调用allure.step
我去官网上看,有一句话是这么写的

This plugin gets automatically connected to py.test via entry point if installed.

Connecting to IDE:

pytest_plugins = 'allure.pytest_plugin',\

意思是链接到IDE 需要进行设置,也没有写怎么设置,我猜想可能是这里的问题,不知道你是否遇见过,或者已经解决了?

孙高飞 #13 · March 09, 2017 作者
徐旻 回复

奇怪啊,你用的什么IDE,我用pycharm没碰见这个问题

我也是pycharm 。
安装都是用pip安装的。

已经查了一个下午的资料了,实在想不出来怎么回事。

我安装好的目录结构是这样的。感觉怪怪的。
你这里安装目录allure的目录是否在pytest里面。

因为官网的例子有

import pytest

def test_foo():
with pytest.allure.step('step one'):
# do stuff

with pytest.allure.step('step two'):
# do more stuff

怎么看allure的目录都是在pytest下面

孙高飞 #15 · March 09, 2017 作者
徐旻 回复

现在是什么错误? IDE提示找不到那个装饰器么?


显示allure里找不到step。

孙高飞 #17 · March 09, 2017 作者
徐旻 回复


好奇怪。。。。我这里好好的。。。。。。。

问题1你是用pip安装的?
问题2 告诉我你sit-package 下面pytest目录和allpure的目录 是平级还是allprue目录是pytest的子目录

孙高飞 #19 · March 09, 2017 作者
徐旻 回复

问题1:我是直接用IDE装的模块
问题2:我记得是平级的

孙高飞 回复

t h x 我再去试试

还有个问题。你是先装的pytest3.0.6,然后在装allprue的时候pytest自动降级为2.9.0的。还是装的时候就是pytest2.9.0

孙高飞 #22 · March 09, 2017 作者
徐旻 回复

降级的。 现在pytest的allure有bug 只支持2.9.0以下版本

孙高飞 回复

用了pycharm 进行安装还是不行。我用的是pycharm community edition ,你这里呢?

孙高飞 #24 · March 09, 2017 作者
徐旻 回复

额,不知道在哪看版本,这玩意还没用熟呢

pytest 没有问题。我已经换了三个姿势安装了,还是不对呀。 郁闷。。

孙高飞 #27 · March 09, 2017 作者
徐旻 回复

你先别管IDE,你用命令行跑一下命令。 排除一下IDE的问题,看看是不是allure没装好

用什么命令跑?

孙高飞 #30 · March 09, 2017 作者
徐旻 回复

py.test --alluredir report

孙高飞 #32 · March 09, 2017 作者
徐旻 回复

你查查怎么装这个插件吧。。。。。

是不是先要安装allure 然后才可以用pytest-allure-adaptor


这个是否就算是 allure安装成功了?我也找到的report 目录,但是问题还是没有解决

@ycwdaaaa yc @ycwdaaaa w @lunamagic 请问大神 我用命令py.test --alluredir report 报这个错是什么情况


在桌面新建了个文件test 然后login.py文件放入 cd到test文件夹里执行命令的

Tester_web 回复

感觉pytest都没有搞定。 你先尝试用pytest 看是不是能收集测试用例,然后执行。
直接用命令 py.test





我pytest版本是2.9.0
@lunamagic 大神帮忙看看 谢谢

@lunamagic

@ycwdaaaa
命令无法执行的问题搞定了 现在就是怎么使用allure生成工具 生成报告呢 windows7环境

提示 not found

请问下,pytest这个的allure报告里,那个title怎么写成中文啊?
@ycwdaaaa

Tester_web 回复

看allure的报告需要启动一个服务。所以最好是集成到jenkins上,那么在jenkins上就需要安装allure的 allure command这个工具。这个你可以在网上找一下教程。

@lunamagic @ycwdaaaa 你们有遇到这个问题没,本来pytest都可以直接运行的脚本,报这个错以后,以前的脚本都跑不了了

ValueError: Plugin already registered: allure_pytest=<module 'allure.pytest_plugin' from '/usr/local/lib/python2.7/site-packages/allure/pytest_plugin.py'>

@Tester_web win7下命令行生成报告搞定了吗?

@Tester_web 搞定了,环境变量设置的问题。。😂

@lunamagic 我也遇到和你一样的问题,你当时怎么解决的?

六星 回复

和我遇上的哪个问题一样?


装完allure,调用不了allure.step
@lunamagic

六星 回复

不需要去管他,开始我也以为这样是不可以运行的,实际上 在pycharm里面就是这样显示的。你先把脚本跑起来,我估计不会报错的。

@ycwdaaaaallure-pytest的说明文档中看到
Note: this plugin currently supports only Allure 1.4.x series.

但是,allure1又停止维护了。这影响使用么?

徐旻 回复

看了下allure的init.py,其实你修改下它的init.py文件 将包括step在内的api全部用all来写,这样应该就可以让IDE识别了...



为啥我跑出来的 report是xml文件 不是html

allure-pytest 有没有 add environment的方法 来增加环境变量信息显示在报告中



这个问题有解决的办法吗各位

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up