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

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

在使用 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 条回复 时间 点赞
bhyuan 回复

我运行没问题

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

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

加多一层

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

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

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

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

bhyuan #10 · 2017年08月02日 Author
Nisir 回复

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

江寒 回复

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

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

不会出现 “用例会运行 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

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

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