完美解决Python 3.0环境 robotframework的appiumlibrary run-on-failure 功能无法使用的问题

大家都知道RobotFramework的第三方测试库非常多,极其好用。引入AppiumLibrary库,被大多数公司作为app自动化测试的框架。

但是AppiumLibrary对于Python3.0的支持目前还是实验阶段。鉴于python2.7将在2020年不再更新,笔者尝试着使用python3.0来大家基于appium的自动化测试框架。

经过笔者测试,发现大部分功能还是OK的,但是在run-on-failure功能上,发现appiumlibrary并没有按照预期的那样在出错时执行设定好的命令。那么这个是什么原因导致的呢?别着急,我们先来看一下库的代码:

在根目录下的__init__.py文件:

    def __init__(self, timeout=5, run_on_failure='Capture Page Screenshot'):
        """AppiumLibrary can be imported with optional arguments.

        ``timeout`` is the default timeout used to wait for all waiting actions.
        It can be later set with `Set Appium Timeout`.

        ``run_on_failure`` specifies the name of a keyword (from any available
        libraries) to execute when a AppiumLibrary keyword fails.

        By default `Capture Page Screenshot` will be used to take a screenshot of the current page.
        Using the value `No Operation` will disable this feature altogether. See
        `Register Keyword To Run On Failure` keyword for more information about this
        functionality.

        Examples:
        | Library | AppiumLibrary | 10 | # Sets default timeout to 10 seconds                                                                             |
        | Library | AppiumLibrary | timeout=10 | run_on_failure=No Operation | # Sets default timeout to 10 seconds and does nothing on failure           |
        """
        for base in AppiumLibrary.__bases__:
            base.__init__(self)
        self.set_appium_timeout(timeout)
        self.register_keyword_to_run_on_failure(run_on_failure)

我们可以看出,在引入appiumLibrary库的时候其实默认调用了register_keyword_to_run_on_failure方法,而且这个方法的参数是有默认值的,也就是Capture Page Screenshot,那么为什么脚本在出错时没有调用呢?

首先我就想到了进入register_keyword_to_run_on_failure这个方法去看源代码,然后并没有发现什么有用的东西。这时候我们进行分析:register_keyword_to_run_on_failure在init方法的时候调用也就是说所有的keyword在失败时都会调用后面的方法,这个是怎样实现的?

所以我们继续分析,进入appiumLibrary的keywords文件夹,我发现,所有的keyword的类都继承于keywordgroup。这让我自然而然的进入了keywordgroup中:

import sys
import inspect
try:
    from decorator import decorator
except SyntaxError:  # decorator module requires Python/Jython 2.4+
    decorator = None
if sys.platform == 'cli':
    decorator = None  # decorator module doesn't work with IronPython 2.6


def _run_on_failure_decorator(method, *args, **kwargs):
    try:
        return method(*args, **kwargs)
    except Exception as err:
        self = args[0]
        if hasattr(self, '_run_on_failure'):
            self._run_on_failure()
        raise err


class KeywordGroupMetaClass(type):
    def __new__(cls, clsname, bases, dict):
        if decorator:
            for name, method in dict.items():
                if not name.startswith('_') and inspect.isroutine(method):
                    dict[name] = decorator(_run_on_failure_decorator, method)
        return type.__new__(cls, clsname, bases, dict)


class KeywordGroup(object):
    __metaclass__ = KeywordGroupMetaClass

不难看出KeywordGroupMetaClass作为元类,限定了类方法中所有的method都套上了一个_run_on_failure_decorator的装饰器。重点关注这行代码的使用方法:

class KeywordGroup(object):
    __metaclass__ = KeywordGroupMetaClass

不难发现,这个是python2的写法,在python3中这样使用,不会报错,也不会有任何作用。

在python3中使用元类的方法是:

class MyList(list, metaclass=ListMetaclass):
    pass

为了兼容python2和python3,我们需要使用以下方法(参考http://python-future.org/compatible_idioms.html):

# Python 2 and 3:
from six import with_metaclass
# or
from future.utils import with_metaclass

class Form(with_metaclass(FormType, BaseForm)):
    pass

我们把这段代码进行如下修改:

from six import with_metaclass
class KeywordGroup(with_metaclass(KeywordGroupMetaClass, object)):
  pass

经过测试,这样就可以既在python2也可以在python3 work了。

 

posted @ 2019-02-27 21:02  海微梦  阅读(1057)  评论(0编辑  收藏  举报