ソフトウェアのテストと開発の戦い|デコレータを書くときに踏まれたいくつかの落とし穴を記録する

この記事はindeyo、ホグワーツのテスト研究所、元のリンクの優秀な学生の研究ノートから再生されます。
http://qrcode.testing-studio.com/f?from=51cto&url=https://ceshiren.com/tag/ %E7%B2%BE%E5%8D%8E%E5%B8%96再印刷のソースを示してください

バックグラウンド

デコレータは、Pythonで非常に便利な構文シュガーであり、繰り返しの多いコードの記述を減らすことができます。
最近、アプリ自動化フレームワークの例外処理について学びました。繰り返しコードがある程度あります。これをテーマとして使用して、デコレータを練習します。
デコレータのパスを記録しましょう。

坑1:ヒント:テストモジュール/パッケージに有効なPython名があることを確認してください。

エラーメッセージ

test_market.py:None (test_market.py)
ImportError while importing test module 'D:\project\Hogwarts_11\test_appium\testcase\test_market.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_market.py:9: in <module>
    from test_appium.page.app import App
..\page\app.py:12: in <module>
    from test_appium.page.base_page import BasePage
..\page\base_page.py:16: in <module>
    from test_appium.utils.exception import exception_handle
..\utils\exception.py:11: in <module>
    from test_appium.page.base_page import BasePage
E   ImportError: cannot import name 'BasePage' from 'test_appium.page.base_page' (D:\project\Hogwarts_11\test_appium\page\base_page.py)

理由

exception.pyファイルとbase_page.pyファイルの間には相互呼び出し関係があります。

解決

ループ呼び出しのパッケージインポート情報を関数に入れます。片方の参照情報が機能に配置されている限り、両側を配置する必要はありません。
exception.pyファイルを変更しただけで、base_page.pyは変更されていません。

exception.py

def exception_handle(func):
    def magic(*args, **kwargs):
        # 防止循环调用报错
        from test_appium.page.base_page import BasePage
        # 获取BasePage实例对象的参数self,这样可以复用driver
        _self: BasePage = args[0]
...

坑2:IndexError:タプルインデックスが範囲外です

エラーメッセージ

test_search.py:None (test_search.py)
test_search.py:11: in <module>
    from test_appium.page.app import App
..\page\app.py:12: in <module>
    from test_appium.page.base_page import BasePage
..\page\base_page.py:52: in <module>
    class BasePage:
..\page\base_page.py:74: in BasePage
    def find(self, locator, key=None):
..\page\base_page.py:50: in exception_handle
    return magic()
..\page\base_page.py:24: in magic
    _self: BasePage = args[0]
E   IndexError: tuple index out of range

理由

デコレータを初めて作成するときに、この間違いを犯すのは本当に簡単です。

def decorator(func):
    def magic(*args, **kwargs):
        _self: BasePage = args[0]
        ...
        return magic(*args, **kwargs)
    # 这里的问题!!!不应该返回函数调用,要返回函数名称!!!
    return magic()

return関数呼び出しがこのエラーを報告するのはなぜですか?
magic()関数が呼び出されたとき、パラメーターは渡されませんが、入力パラメーターはmagic()で引用されます。現時点では、argsには値がないため、args [0]は当然使用できません。

解決

ブラケットを外すだけ

def decorator(func):
    def magic(*args, **kwargs):
        _self: BasePage = args[0]
        ...
        return magic(*args, **kwargs)
    # 返回函数名,即函数本身
    return magic

ピット3:例外処理は1回だけ実行され、自動化を続行できません

エラーメッセージ

NoSuchElementException、TimeoutException、その他の一般的な問題など、要素の検索プロセスにおける主にさまざまな例外。

理由

例外処理後、再帰ロジックが正しく記述されていません。return func()はfunc()を実行し、例外処理ロジックからジャンプするため、例外処理は1回だけ実行されます。
正しい書き方はreturnmagic()です。
デコレーターのシャオバイがまた間違えたような気がします...えーと...

解決

直感的には、重要でないコードはフィルタリングされており、例外処理ロジックコードは記事の最後にリリースされます。

def exception_handle(func):
    def magic(*args, **kwargs):
        _self: BasePage = args[0]
        try:
            return func(*args, **kwargs)
        # 弹窗等异常处理逻辑
        except Exception as e:
            for element in _self._black_list:
                elements = _self._driver.find_elements(*element)
                if len(elements) > 0:
                    elements[0].click()
                    # 异常处理结束,递归继续查找元素 
                    # 这里之前写成了return func(*args, **kwargs),所以异常只执行一次!!!!!
                    return magic(*args, **kwargs)
            raise e
    return magic

ピット4:ドライバーを再利用する方法は?

問題

私が最初にデコレータを書き込もうとしたとき、私は問題を見つけました。
デコレータにはFind_elementsが必要ですが、ドライバはどこから来たのですか?そして、BasePageのプライベート変数error_maxとerror_countを取得する方法は?BasePageオブジェクトを作成しますか?次に、ドライバーをfunc関数に渡しますか?
funcのドライバーはプライベートであり、外部から呼び出すことはできません(emmmである可能性があります...)。
異常に関連する変数を公開しようとしましたが、役に立たず、find_elementsの呼び出しの問題を解決できませんでした。

解決

Sihanのアプローチは、デコレータで自己変数を作成し、関数funcの最初のパラメータselfであるargs [0]を取得することです。
_self:BasePage = args [0]この単純な文は、私のすべての質問にうまく答えました。
クラス関数定義では、selfはクラス自体を表すため、._ driver属性を取得してfind_elementsを呼び出すことができます。

坑5:AttributeError

要素を見つけた後にクリックしようとしているときにエラーを報告する

エラーメッセージ

EINFO:root:('id', 'tv_search')
INFO:root:None
INFO:root:('id', 'image_cancel')
INFO:root:('id', 'tv_agree')
INFO:root:('id', 'tv_search')
INFO:root:None

test setup failed
self = <test_appium.testcase.test_search.TestSearch object at 0x0000018946B70940>

    def setup(self):
>       self.page = App().start().main().goto_search()

test_search.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <test_appium.page.main.MainPage object at 0x0000018946B70780>

    def goto_search(self):
>       self.find(self._search_locator).click()
E       AttributeError: 'NoneType' object has no attribute 'click'

..\page\main.py:20: AttributeError

理由

find関数を確認した後、要素を検索した後、要素自体を返します。

@exception_handle
    def find(self, locator, key=None):
        logging.info(locator)
        logging.info(key)
        # 定位符支持元组格式和两个参数格式
        locator = locator if isinstance(locator, tuple) else (locator, key)
        WebDriverWait(self._driver, 10).until(expected_conditions.visibility_of_element_located(locator))
        element = self._driver.find_element(*locator)
        return element

つまり、デコレータが間違っています

def exception_handle(func):
    def magic(*args, **kwargs):
        _self: BasePage = args[0]
        try:
            # 这里只是执行了函数,但是没有return
            func(*args, **kwargs)
        # 弹窗等异常处理逻辑
        except Exception as e:
            raise e
    return magic

解決

関数呼び出しはデコレータで返される必要があります。そうでない場合、関数自体の戻りはデコレータによって食べられます。

def exception_handle(func):
    def magic(*args, **kwargs):
        _self: BasePage = args[0]
        try:
            # return函数执行结果
            return func(*args, **kwargs)
        # 弹窗等异常处理逻辑
        except Exception as e:
            raise e
    return magic

思考:デコレータを書くとき、さまざまなリターンが少し目がくらむように見えました。各関数を返すことができますが、それはどういう意味ですか?

def exception_handle(func):
    def magic(*args, **kwargs):
        _self: BasePage = args[0]
        try:
            # 第1处 return:传递func()函数的返回值。如果不写,原有return则失效
            return func(*args, **kwargs)
        # 弹窗等异常处理逻辑
        except Exception as e:
            for element in _self._black_list:
                elements = _self._driver.find_elements(*element)
                if len(elements) > 0:
                    elements[0].click()
                    # 异常处理结束,递归继续查找元素 
                    # 第2处 return:递归调用装饰后的函数。magic()表示新函数,func()表示原函数,不可混淆
                    return magic(*args, **kwargs)
            raise e
    # 第3处 return:返回装饰后的函数,装饰器语法。不能返回函数调用magic()
    return magic

デコレータの完全な実装

exception.py

import logging

logging.basicConfig(level=logging.INFO)

def exception_handle(func):
    def magic(*args, **kwargs):
        # 防止循环调用报错
        from test_appium.page.base_page import BasePage
        # 获取BasePage实例对象的参数self,这样可以复用driver
        _self: BasePage = args[0]
        try:
            # logging.info('error count is %s' % _self._error_count)
            result = func(*args, **kwargs)
            _self._error_count = 0
            # 返回调用函数的执行结果,要不然返回值会被装饰器吃掉
            return result
        # 弹窗等异常处理逻辑
        except Exception as e:
            # 如果超过最大异常处理次数,则抛出异常
            if _self._error_count > _self._error_max:
                raise e
            _self._error_count += 1
            for element in _self._black_list:
                # 用find_elements,就算找不到元素也不会报错
                elements = _self._driver.find_elements(*element)
                logging.info(element)
                # 是否找到弹窗
                if len(elements) > 0:
                    # 出现弹窗,点击掉
                    elements[0].click()
                    # 弹窗点掉后,重新查找目标元素
                    return magic(*args, **kwargs)
            # 弹窗也没有出现,则抛出异常
            logging.warning("no error is found")
            raise e
    return magic

学習経験

最初にシハンの説明を読まないで、あなた自身の理解に従ってデコレータを書くのが最善です。そうすれば、学習効果が最高になります。
問題に遭遇したときに問題を解決しようとしましたが、踏んだピットが印象的でした。
Sihanの解決策をもう一度参照する手がかりは実際にはありません。そうすると、突然の明確さの感覚が生まれます。
現在、これらのピットを踏んでいますが、抜けがあれば追加してください〜

この記事はindeyo、ホグワーツのテスト研究所、元のリンクの優秀な学生の研究ノートから再生されます。
http://qrcode.testing-studio.com/f?from=51cto&url=https://ceshiren.com/tag/ %E7%B2%BE%E5%8D%8E%E5%B8%96再印刷のソースを示してください

おすすめ

転載: blog.51cto.com/14293469/2561925