如何修改pytest-html源码来优化接口自动化测试报告

论坛 期权论坛 期权     
柠檬班软件测试   2019-6-9 21:28   948   0

你这么优秀,一定只想把“柠檬班”置顶



前言
大家好,我是柠檬班Python6期的学员小翟。

以前常用unittest做接口自动化测试,后来想获取单个接口的响应时间,便采用pytest单元测试框架。

虽然获得的时间不太准确。

因为pytest测试报告的时间是从一个用例开始到结束,其中包括了断言,所以实际时间比响应时间略长。

如果从参考的角度看,这个时间虽然不是很准确,但仍有价值。

Pytest与ddt冲突
如果你想使用pytest,就不能使用ddt做数据驱动了,否则运行时会抛出异常。

提示@data不是pytest的一个fixture

这时可以使用pytest的参数化,即下面这种形式:




一般来说,使用最多的是前两个参数,argnames和argvalues。

argnames:用逗号分隔的字符串,表示一个或多个参数名或参数列表/元组。

argvalues:是一个列表,让前面的参数argnames依次从中取值用于迭代




这样的话,我们就解决了第一个问题。

即用pytest参数化来代替unittest的数据驱动。

测试报告中的用例名字不规范
迫不及待的点了运行,突然发现测试报告中的用例名字很糟糕,这是什么鬼,为什么会这样写?






这样的测试用例名字,第一眼让人感觉很凌乱,因为根本看不出是哪条接口的。

如果能将测试用例中的case_id对应的值作为用例名字就比较完美了:




当时我的第一思路是,先去看看测试报告的元素。




这个用例名字的元素对应的标签的td。

对应的class属性值是"col-name"。

那能不能去pytest-html的源码中看看在哪一块。




在其他地方没有找到,但在plugin.py中找到了,可以看到找到了一个,没错就是我们想要的td标签。

那么还有一个参数self.test_id是什么?

大胆的猜测一下,应该是td标签对应的文本值吧。




既然要改变td标签的文本值,现在已经猜测是self.test_id对应的值。

接下只需搜索一下self.test_id是怎么定义的,一切便迎刃而解!

图1:

[h1]

[/h1]
图2:





图3:




但是真的这么简单吗?

请看上图,一共找到了4个self.test_id。

除了刚刚我们找的那个,有2个和report.nodeid有关。

有1个和extra_index和test_index有关,report.nodeid可能是report的一个属性。

而后面两个参数是crate_asset的两个形参,这些东西我们能直接获取到吗?

看到这里,感觉第一个思路错了,至少是方向有问题!

想想之前,在做unittest的时候,ddt通过修改源码改变用例名字。

ddt修改时并不是去HTMLTestRunner中修改的。

同理,我们推断下,pytest要修改用例名字,关注点应该是参数化,而不是pytest-html的源码。




这样就获得了第二个思路,去查看parametrize()这个函数的源码。

查看源码
在查看源码过程中,偶然看到另一个函数是这样定义的:




特别是ids[index] = testid + str(counters[testid]),是不是特别熟悉?

对了,这个就是测试报告里的[data0],[data1]...

而且看看counters的怎么定义的,参数是匿名函数lambda,默认是从0开始。

看到这里,大概就比较清楚了,我们最重要的关注点应该是ids,而ids恰好是parametrize()函数的一个参数。




源码给出的解释是:ids是一个字符串列表或可调用的字符串。

如果是字符串列表,则每个字符串对应argvalues,它们是测试id的一部分。

如果是None,则会使用自动生成的id。如果是可调用的...

看这么两句就可以了,如果是None,会自动生成用例名字(测试id就是我们说的用例名字),也就是我们看到的[data0],[data1]...。

如果有100条用例,那么第100条的用例名字就是[data99]。

如果是列表,会将列表中的每一次迭代的值作为用例名字

这下我们知道如何修改用例名字了。

直接给@pytest.mark.parametrize()添加ids参数就行。



[h2][/h2]利用JS优化测试用例名字
经过第二步的优化,我们满足了最基本的需求。

即想要的用例名字必须是测试用例里自己定义的,而不是一系列默认生成的值。



但问题又来了,看报告中前面一长串,真的很不美观。

如何去掉前面的
TestCases/test_my_requests.py::TestMyRequests::test_send_requests。

首先考虑去源码里直接定位,但根据之前的经验,源码里这一串可能就是一个属性的值,你能得到的无非就是类似于report.属性的这种方式。

何况,在对pytest和pytest-html运行原理不了解的前提下,这样无疑在浪费时间

这时看到了js,pytest所在位置的resources目录下有个main.js。

我们可以通过js来修改td标签中的文本内容。





可以在main.js定义一个函数change_testName()。

提取字符串
通过split()方法来提取字符串

TestCases/test_my_requests.py::TestMyRequests::test_send_requests[checkUpdate_normal]中的checkUpdate_normal。

当然你也可以用正则




pytest-html加载js的原理是,它会将resources目录下的main.js中加载到最终的html测试报告的中。

这是生成的html报告源码:






既然不需要我们主动引入js,只要在main.js中定义好函数就行,但是却发现好像自定义的js函数不起作用。

此道不通,必须换另一条道。

既然main.js中定义了那么多js函数,我们是否可以将自定义的js语句放到一个已知的函数里面。

当然前提是变量不能有重名,不能影响已知函数的功能。

找来找去,发现了有个疑似初始化的函数init()。

所以放在这个函数里比较合适




调整之后的init函数



[h2][/h2][h2]大功告成:还差点什么[/h2]
经过运行之后,发现我们的目的已经达到,既可以获得单个接口的测试时间,又调整了用例名字。

比原来美观多了 唯一的建议修改源码,最好使用pipenv创建一个虚拟环境。

因为多个项目之间共用pytest-html的话,一旦修改了pytest-html源码中的js,那么所有的项目样式都会变。

为了保持多个项目依赖库之间不受干扰,请使用虚拟环境。



这样,我们的修改就完成了吗?

请仔细看我们添加的js代码:




诚然,当参数化的时候,它没有问题。

可是有的场景我们不需要参数化,比如设计一个test_update_normal的测试用例




得到的测试报告是这样的:



有没有一种很丑的感觉,第3条用例的名字不符合我们的要求。

这是因为参数化生成的用例名字包含[],所以我们才能用js对[]做分割,不使用参数化的用例没有[],这时候再去调用js就抛异常了,等于什么都没改变

既然js有异常行为,相应的就该有捕获异常的方法。

try...catch这时候可以用起来了,捕获异常,再对异常情况添加处理方式




再次生成的测试报告长这样子:




这时感觉用例名字标准多了,我们修改源码。

不但要达到基本目的,更重要的是维护源码的健壮性。

如果因为修改引起的源码只能在少数情况下使用,那么这样做等于是破坏。

另外,再次说明一下,请在虚拟环境下尝试源码修改。





今日福利
需要接口自动化测试环境搭建学习视频

可加小米老师微信:
13327316731
暗号:公众号










分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:
帖子:
精华:
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP