本当に自動化する方法を知っていますか? Web 自動テスト - PO モードの実践、徹底した記事...


序文

POモード

ページ オブジェクト (略して PO) パターンは、Selenium の実践で最も人気があり、自動テストで最もよく知られ、尊敬されている設計パターンです。自動テストを設計する場合は、ページに応じてページ要素とその操作メソッドを抽象化し、特定のオブジェクトに分離して整理します。

Webオートメーションで最も厄介な問題の1つはページの変更ですが、POデザインパターンを使用していない場合、ページが変更されると、これまでの要素の配置や要素の操作方法さえも使用できなくなり、修正が必要になります。

テストスクリプトから修正が必要な要素の配置方法や要素の操作方法を一つ一つ見つけ出し、一つ一つ修正していく必要があります。このような自動スクリプトは面倒なだけでなく、維持コストも非常に高くなります。

ページ オブジェクト モードは、この問題を非常にうまく解決できます。利点:
コードの冗長性が削減され、
ビジネスと実装が分離され、
メンテナンス コストが削減されます。

では、ページ オブジェクト モードとは何ですか? 名前が示すとおり、ページ オブジェクトです。実際の自動テストでは、スクリプトは通常 3 つの層に分かれています: オブジェクト層: ページ要素の保存に使用され、位置決めロジック層: パッケージ化された要素の保存に使用され
ます
。機能的ユースケースモジュール
ビジネス層: 実際のテストケースの運用部分を保存するために使用されます。

上記の 3 つの層に加えて、基本層もあります。基本層は主に Selenium のいくつかの一般的なメソッド用です。クリック、入力、その他の操作など、実際のビジネス ニーズに応じて再カプセル化され、待機が追加されます。 、ログ入力、スクリーンショット、その他の操作を容易にするため、スクリプトの実行ステータスを確認し、問題のトラブルシューティングを行います。

ベースレイヤー

基本レイヤー クラス名は通常、BasePage という名前です。要素に対する後続のオブジェクト レイヤー操作は、この基本クラスを継承します。次の例では、クリックと入力を使用します。

# basepage.py
import os
import time
import datetime
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common.logging import log
from common.constant import IMG_DIR


class BasePage:

    def __init__(self, driver: WebDriver):
        self.driver = driver

    def wait_ele_visible(self, loc, img_desc, timeout=20, frequency=0.5):
        """等待元素可见"""
        try:
            WebDriverWait(self.driver, timeout, frequency).until(EC.visibility_of_element_located(loc))
            log.info("等待:{} - 元素{}可见成功。".format(img_desc, loc))
        except:
            log.exception("等待:{} - 元素{}可见失败!".format(img_desc, loc))
            self.save_img(img_desc)
            raise

    def get_element(self, loc, img_desc):
        """查找元素"""
        try:
            ele = self.driver.find_element(*loc)
        except:
            log.exception("查找:{} - 元素{}失败!".format(img_desc, loc))
            self.save_img(img_desc)
            raise
        else:
            log.info("查找:{} - 元素{}成功".format(img_desc, loc))
            return ele

    def click_element(self, loc, img_desc, timeout=20, frequency=0.5):
        """点击元素"""
        self.wait_ele_visible(loc, img_desc, timeout, frequency)
        ele = self.get_element(loc, img_desc)
        try:
            ele.click()
            log.info("点击:{} - 元素{}成功".format(img_desc, loc))
        except:
            log.exception("点击:{} - 元素{}失败!".format(img_desc, loc))
            self.save_img(img_desc)
            raise

    def input_text(self, loc, value, img_desc, timeout=20, frequency=0.5):
        """在元素中输入文本"""
        self.wait_ele_visible(loc, img_desc, timeout, frequency)
        ele = self.get_element(loc, img_desc)
        try:
            ele.send_keys(value)
            log.info("输入:在{} - 元素{}输入文本值({})成功".format(img_desc, loc, value))
        except:
            log.exception("输入:在{} - 元素{}输入文本值({})失败!".format(img_desc, loc, value))
            self.save_img(img_desc)
            raise

    def save_img(self, img_description):
        """保存异常截图"""
        now = time.strftime("%Y-%m-%d %H-%M-%S ", time.localtime())
        img_path = os.path.join(IMG_DIR, now + img_description + '.png')
        try:
            self.driver.save_screenshot(img_path)
        except:
            log.exception("异常截图失败!")
        else:
            log.info("异常截图成功,截图存放在{}".format(img_path))

click_element() を例にとります。ここでは、待機中の操作、ログ入力、および例外のスクリーンショットが 2 次カプセル化に追加されています。後で要素をクリックすると、click_element() を直接呼び出して 1 ステップで正しく取得できます。待機、ログ、例外を考慮する必要はなく、すべてがここで処理されています。

初期段階では基本的なページを作成するのに時間がかかりますが、基礎を構築しておけば、その後のメンテナンス作業は非常に簡単になります。上記は単なる例であり、実際のニーズに応じて最適化できます。

オブジェクト層とロジック層

オブジェクト層にはページ要素の配置が格納され、ロジック層には要素の操作メソッド(ページ関数)が格納されます。要素の配置は、実際のニーズに応じて別のモジュールに保持することも、Excel に格納して集中管理することもできます。

以下は、要素の配置および要素の操作メソッドが 1 つのモジュール (ページごとに 1 つのモジュール) に格納されていることを示しています。後続のページ要素が変更された場合は、このモジュール内の対応する配置式または操作メソッドを変更するだけで済みます。

このデモでは、Baidu ホームページを例として取り上げます。

# baidu_page.py

from selenium.webdriver.common.by import By
from common.basepage import BasePage


class LoginPage(BasePage):

    login_btn = (By.XPATH, '//div[@id="u1"]//a[@name="tj_login"]')  # 登录按钮
    username_login_btn = (By.ID, 'TANGRAM__PSP_11__footerULoginBtn')    # 用户名登录按钮
    user_input = (By.ID, 'TANGRAM__PSP_11__userName')  # 用户信息输入框
    pwd_input = (By.ID, 'TANGRAM__PSP_11__password')  # 密码输入框
    login_submit = (By.ID, 'TANGRAM__PSP_11__submit')   # 登录提交按钮

    def login(self, user, pwd):
        """
        百度用户名登录
        :param user: 手机/邮箱/用户名
        :param pwd: 密码
        :return:
        """
        self.click_element(self.login_btn, '百度-登录')
        self.click_element(self.username_login_btn, '百度登录-用户名登录')
        self.input_text(self.user_input, user, '用户名登录-手机/邮箱/用户名')
        self.input_text(self.pwd_input, pwd, '用户名登录-密码')
        self.click_element(self.login_submit, '用户名登录-登录')

ビジネス層

実際のテスト ケースの操作を保存するために使用されます。要素の配置およびページ関数はここには表示されません。すべての操作はロジック層に直接呼び出されます。

テスト ケース = テスト オブジェクトの関数 + テスト データ。以下では、例として Baidu ログインを取り上げます (デモンストレーションのため、省略しています)。

import unittest
import pytest
import ddt
from selenium import webdriver
from PageObjects.baidu_login_page import LoginPage
from testdatas import common_datas as com_d
from testdatas import login_data as lo_d
from common.logging import log


@ddt.ddt
class TestLogin(unittest.TestCase):

    def setUp(self):
        log.info("-------用例前置工作:打开浏览器--------")
        self.driver = webdriver.Chrome()
        self.driver.get(com_d.baidu_url)
        self.driver.maximize_window()

    def tearDown(self):
        self.driver.quit()
        log.info("-------用例后置工作:关闭浏览器--------")

    @pytest.mark.smoke
    def test_login_success(self):
        # 用例:登录页的登录功能
        # 步骤
        LoginPage(self.driver).login(lo_d.success_data['user'], lo_d.success_data['pwd'])
        # 断言.....

操作結果:

Testing started at 11:50 ...
C:\software\python\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.3\helpers\pycharm\_jb_unittest_runner.py" --path D:/learn/test/testcases/test_baidu_login.py
Launching unittests with arguments python -m unittest D:/learn/test/testcases/test_baidu_login.py in D:\learn\test\testcases

Process finished with exit code 0
2021-03-14 11:50:47,238-【test_baidu_login.py-->line:27】-INFO:-------用例前置工作:打开浏览器--------
2021-03-14 11:50:51,327-【basepage.py-->line:38】-INFO:等待:百度-登录 - 元素('xpath', '//div[@id="u1"]//a[@name="tj_login"]')可见成功,耗时0:00:00.056843秒
2021-03-14 11:50:51,339-【basepage.py-->line:77】-INFO:查找:百度-登录 - 元素('xpath', '//div[@id="u1"]//a[@name="tj_login"]')成功
2021-03-14 11:50:51,414-【basepage.py-->line:86】-INFO:点击:百度-登录 - 元素('xpath', '//div[@id="u1"]//a[@name="tj_login"]')成功
2021-03-14 11:50:53,463-【basepage.py-->line:38】-INFO:等待:百度登录-用户名登录 - 元素('id', 'TANGRAM__PSP_11__footerULoginBtn')可见成功,耗时0:00:02.048293秒
2021-03-14 11:50:53,474-【basepage.py-->line:77】-INFO:查找:百度登录-用户名登录 - 元素('id', 'TANGRAM__PSP_11__footerULoginBtn')成功
2021-03-14 11:50:53,535-【basepage.py-->line:86】-INFO:点击:百度登录-用户名登录 - 元素('id', 'TANGRAM__PSP_11__footerULoginBtn')成功
2021-03-14 11:50:53,576-【basepage.py-->line:38】-INFO:等待:用户名登录-手机/邮箱/用户名 - 元素('id', 'TANGRAM__PSP_11__userName')可见成功,耗时0:00:00.040890秒
2021-03-14 11:50:53,584-【basepage.py-->line:77】-INFO:查找:用户名登录-手机/邮箱/用户名 - 元素('id', 'TANGRAM__PSP_11__userName')成功
2021-03-14 11:50:53,714-【basepage.py-->line:98】-INFO:输入:在用户名登录-手机/邮箱/用户名 - 元素('id', 'TANGRAM__PSP_11__userName')输入文本值(15692004245)成功
2021-03-14 11:50:53,759-【basepage.py-->line:38】-INFO:等待:用户名登录-密码 - 元素('id', 'TANGRAM__PSP_11__password')可见成功,耗时0:00:00.043882秒
2021-03-14 11:50:53,771-【basepage.py-->line:77】-INFO:查找:用户名登录-密码 - 元素('id', 'TANGRAM__PSP_11__password')成功
2021-03-14 11:50:53,925-【basepage.py-->line:98】-INFO:输入:在用户名登录-密码 - 元素('id', 'TANGRAM__PSP_11__password')输入文本值(phang0209)成功
2021-03-14 11:50:53,958-【basepage.py-->line:38】-INFO:等待:用户名登录-登录 - 元素('id', 'TANGRAM__PSP_11__submit')可见成功,耗时0:00:00.031914秒
2021-03-14 11:50:53,969-【basepage.py-->line:77】-INFO:查找:用户名登录-登录 - 元素('id', 'TANGRAM__PSP_11__submit')成功
2021-03-14 11:50:54,051-【basepage.py-->line:86】-INFO:点击:用户名登录-登录 - 元素('id', 'TANGRAM__PSP_11__submit')成功
2021-03-14 11:50:56,426-【test_baidu_login.py-->line:35】-INFO:-------用例后置工作:关闭浏览器--------


Ran 1 test in 9.191s

OK

出力されたログから判断すると、操作の各ステップが明確に表示され、問題をすぐに特定でき、実際のニーズに応じて最適化できます。

以下は、私がまとめた 2023 年の最も包括的なソフトウェア テスト エンジニア学習ナレッジ アーキテクチャ システム図です。

1. Python プログラミングの入門から習熟まで

画像の説明を追加してください

2. インターフェース自動化プロジェクトの実践

画像の説明を追加してください

3. Web自動化プロジェクトの実戦

画像の説明を追加してください

4. アプリ自動化プロジェクトの実践

画像の説明を追加してください

5. 一流メーカーの履歴書

画像の説明を追加してください

6. DevOps システムのテストと開発

画像の説明を追加してください

7. 一般的に使用される自動テストツール

画像の説明を追加してください

8. JMeter パフォーマンス テスト

画像の説明を追加してください

9. まとめ(最後にちょっとしたサプライズ)

困難を受け入れ、夢を追い、苦労は達成の鍵です。忍耐力、自分自身を超え、成功は継続的な努力によって築かれます。自分の能力を信じ、勇敢に前進し、汗と知恵で輝かしい人生の一章を書き、自分自身の光と栄光を咲かせましょう。

意志を研ぎ澄まし、束縛を打ち破り、奮闘することが栄光を生み出す唯一の方法であり、夢を持ち続け、常識を超え、達成は内なる熱意にかかっています。自分の力を信じ、卓越性を追求し、汗と知恵を絞って人生の壮大な絵を描き、あなた自身の不滅の伝説を作り上げましょう。

魂の情熱を燃やし、夢の翼を放ち、闘いは奇跡を起こすリズム、信念を貫き勇敢に前進、成果は忍耐にある。自分の才能を信じて、卓越性を追求してください。

おすすめ

転載: blog.csdn.net/m0_60054525/article/details/132043777