新手区 给测试用例添加多个装饰器的问题 (python unittest.TestCase)

bhyuan · 2017年08月01日 · 最后由 Nisir 回复于 2017年09月05日 · 2632 次阅读

在使用 unittest 写用例的时候,想要实现:在用例执行的过程中,出现任何错误或失败,就进行截图……代码如下:

#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from parameterized import parameterized
import unittest,time
class BaiduTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)
        self.base_url = "https://www.baidu.com"

    #自己写的截图装饰器
    def screenshot_error(func):
        def _screenshot_error(self,*args,**kwargs):
            try:
                func(self,*args,**kwargs)
            except Exception as e:
                self.driver.get_screenshot_as_file("error.png")
                raise e
        return _screenshot_error

    @screenshot_error
    #@parameterized.expand([(u"自动化测试")])
    def test_baidu(self,key="python"):
        u'''baidu搜索测试'''
        driver = self.driver
        driver.get(self.base_url)
        driver.find_element_by_id('kw').send_keys(key,Keys.ENTER)
        time.sleep(1)
        title = driver.title
        self.assertEqual(key,title)  #错误的断言,验证截图功能

    def tearDown(self):
        self.driver.close()

if __name__=="__main__":
        unittest.main()

运行通过,截图保存成功……

于是再添加 @parameterized.expand([(u"自动化测试")])装饰器,现在运行就有问题了,用例会运行 2 次,第一次是一个空用例,截下一张空白图片,第二次不会截图……出错信息:

Traceback (most recent call last):
File "/home/bhyuan/selenuim-python/test_baidu.py", line 49, in _screenshot_error
raise e
File "/home/bhyuan/selenuim-python/test_baidu.py", line 45, in _screenshot_error
func(self,*args,**kwargs)
TypeError: 'NoneType' object is not callable

本人初学者,感觉问题应该在此处

def _screenshot_error(self,*args,**kwargs):

不知如何修改?

其实两个装饰器后,装饰器内部运行逻辑,我是十分懵逼啊,希望有共同需求的童鞋一起探讨,期待大神指点~~~

备注:parameterized 是通过 $ pip install parameterized 安装的

共收到 13 条回复 时间 点赞

我觉得是不是 parameterized 这个装饰器,它本身是没有返回的。把这个装饰器和截图装饰器的顺序换一下,然后截图装饰器里 func()前应该加 return 试试。

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

两个装饰器是有执行顺序的,我以前试过,现在忘了。。。你可以写个小 demo 试下嘛

这个时候就需要 debug 运行看问题在哪里

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

Nisir 回复

parameterized 是有返回值的,我也交换过两个装饰器的顺序,没用

江寒 回复

写 demo 也是写出这种效果,给每一行都去加 print 了,还是不知道问题出在哪里,没有解决思路……😂

def screenshot_error(param,*args,**kwargs):
    def decorate(func):
        def _screenshot_error(self,*args,**kwargs):
            try:
                func(self,*args,**kwargs)
            except Exception as e:
                self.driver.get_screenshot_as_file("error.png")
                raise e
        return _screenshot_error
    return decorate

加多一层

没用,早试过了,报错还是一样的……

bhyuan 回复

我运行没问题

不会出现 “用例会运行 2 次,第一次是一个空用例,截下一张空白图片,第二次不会截图……” 的现象?
如果是,可以把你的完整源码给我参考下么?小白,请谅解~~~

你这个多加一层写得肯定有错误,两个 args,两个 kw 都混淆了。 内部完全无法识别装饰器函数自身的参数。

你参数化有问题吧,你在看看参数化这个方法。运行两次是对的,因为你 key 有两个值。

Nisir 回复

你好好理解一下装饰器再说吧。

def screenshot_error(param,*args,**kwargs):
    def decorate(func):
        def _screenshot_error(self,*args,**kwargs):
            try:
                func(self,*args,**kwargs)
            except Exception as e:
                self.driver.get_screenshot_as_file("error.png")
                raise e
        return _screenshot_error
    return decorate

你这段代码第一行的 def screenshot_error(param,args,kwargs) 里面的 args 跟 kwargs 根本不会起作用的。try 里面的语句 func(self,*args,kwargs) 用到的 args 和 kwargs 使用的是最内层的 def _screenshot_error(self,*args,*kwargs) 里定义的参数。

我知道装饰器是可以带自身参数,你可以把你的代码改成:

def screenshot_error(param, *ag, **kw):
    def decorate(func):
        @functools.wraps(func)
        def wrapper(self,*args,**kwargs):
            try:
                func(self,*args,**kwargs)
            except Exception as e:
                self.driver.get_screenshot_as_file("error.png")
                raise e
        return wrapper
    return decorate

然而楼主的问题不在于要多加一层

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册