インターフェース自動テストフレームワーク構築のためのアイデア分析と技術ポイント

背景

なぜ自動テストを行うのでしょうか?

テスト時間を節約: プロジェクトが一定の規模に達した後の回帰テストなどのシナリオでは、多くの時間と人的資源が必要になります。

なぜインターフェース自動化テストを行う必要があるのでしょうか?

UI に比べてインターフェースが安定しており、自動化の実装が容易で、コード作成後のユースケースやコードのメンテナンスも容易です。

1. 逆アセンブルインターフェーステストのプロセスとシナリオ

1. インターフェースリクエストの内容を解析する

  • l リクエストメソッドメソッド
  • l リクエストアドレス URL
  • l 環境設定IP
  • l 操作前
  • l リクエストボディ(データ型)
  • l ステータスコード
  • l レスポンスボディ
  • l アサーション
  • l 手術後

2.  2 つのインターフェイス テスト シナリオ

  1. 単一のインターフェースを検証する

インターフェイス フィールドのデータ型/長さ/null 値などのチェックポイントが検証されます。このシナリオでは、多数の異なるデータが同じインターフェイスを要求するため、データ駆動型 (DDT) に適しています。このシナリオは主に問題を解決します。ユースケースデータのバッチ読み取り、DDT のロード、実行の記録の結果 (例外処理を使用)、3 つの問題が発生します。

  1. インターフェースのビジネスフローをテストする

このシナリオには、複数のインターフェイスとデータの挿入とクリーニングなどが含まれます。

1取引関係の分析

フローチャート、4+1 ビュー、設計ドキュメントなどから始めることができます。

2 カーディング

例: スタートインジケーター

インジケーターを開始する条件: インジケーターが存在し (delete_flag=0)、インジケーターのステータスが非アクティブである必要があります。

ユースケースの独立性 (分離) を確保するには、テスト プロセスを次のようにする必要があります。

1新しいインジケーターを作成し、インジケーターIDを返します

2 新しく作成されたインジケーターを開始するようにインジケーター起動インターフェイスにリクエストします。アサーション インターフェイスは成功を返します/データベース内のインジケーター ステータスがアクティブに更新されます。インジケーターの起動プロセスとプラットフォーム構築タスクには時間がかかるため、待機を設定する必要があります。ここ。

3. データ クリーニング。インジケーター停止インターフェイスにインジケーター タスクを停止するように要求し、このインジケーターに関連するデータ (インジケーター テーブル/インジケーター式テーブル) を削除します。

2.インターフェースの自動テスト活動を分析する

1.インターフェース通信 -- ユースケース作成の準備

インターフェースのドキュメント

Postman (Postman の機能を柔軟に使用してコードを生成)

ドキュメントを参照し、postman を使用してインターフェースを要求して開きます。

2. 試験プロセスに関連するデータの管理と整理

1. ユースケースの作成

ユースケースデータ ユースケース番号、ユースケース名、URL、メソッド、入力パラメータ、期待される結果

2. ユースケースファイルの形式、編成方法excel/pythonファイル/yaml

1 Excel テーブルを使用してユース ケースを作成し、ユース ケース テーブルのフィールドのデザインに注意してください。

利点: メンテナンスが簡単、変更が簡単、プログラミングは不要

短所: プログラムを読み取る前に、関連するモジュール (openpyxl/xlrd など) でサポートされている必要があり、関連するコードを記述する必要があります。

例:

 データ駆動型の単一インターフェース検証に最適

正規表現とリフレクションを使用したビジネス シナリオのテストに最適

2 Python ファイルを使用してユースケースを作成する

オブジェクト指向および継承されたメソッドを使用してユースケース データを書き込む

利点: Python は直接読み取ることができ、中間層は必要なく、開発環境で直接書き込むことができます。

短所: 作成者は Python 構文を理解し、オブジェクト指向や継承などを理解する必要があります。言語をまたぐとさらに面倒です。

class Case:
    #此处可使用类属性放置所有用例实例共有的属性,如IP,但是IP这种环境配置还是建议放在config里,不然用例改环境太麻烦
    host_ip=""
    def __init__(self,num,name,url,method,req_body,exp_result):
        self.num=""
        self.name=""
        self.url=""
        self.method=""
        self.req_body=""
        self.exp_result=""
#将用例属性进行实例化传入,因为整个框架设计数据传递主要使用json,所以建议所有字符串都用双引号
#实例化用例后导入到其他模块使用就可以了
case_01=Case("01","case_01","/abc","post","{"user":"001"}","{"status":"success"}")
#多个用例时,可使用list传递,适用DDT
case_02=Case("01","case_01","/abc","post","{"user":"001"}","{"status":"success"}")
case_03=Case("01","case_01","/abc","post","{"user":"001"}","{"status":"success"}")
case_box=[case_01,case_02,case_03]
#当然,也可以写个循环生成用例,case_box.append()循环往里加

class New_case(Case):

    def __init__(self,num,name,url,method,req_body,exp_result,exp_code):      super().__init__(num,name,url,method,req_body,exp_result)      self.exp_code = exp_code
#使用继承可以方便添加新的用例属性

3 yaml ファイルを使用してユースケースを作成する

利点: 言語を超えた強力な利点

欠点: ライターは YAML 構文を知っている必要があり、データを読み取るためにサードパーティのモジュール サポートを使用する必要があります。

# yaml语法
# key: {key1: value1,key2: value2} 注意:冒号后有空格,字符串建议使用双引号
case_data_workbook:
  workbook: "../data_of_cases/case2.xlsx"
  worksheet: "Sheet1"

# 用例编写
case_01:
  num: "01"
  name: "case_01"
  url: "/abc/o1"
  req_body : '{"credentials": "123456","account":"admin"}'

3. 環境構成設定

1 Python クラスを使用して構成ファイルを作成する

メリットとデメリットは上記と同じです。例を直接見てみましょう。

"""
建立一个类
配置项用类属性来写,方便其他模块导入使用
路径可以使用pathlib拼接

"""


class CONFIG:  #环境host
    HOST = "http://www.xxx.com:8108"  #用例excel文件
    WORKBOOK = "../data_of_cases/case2.xlsx"  #用例sheet
    WORKSHEET = "Sheet1"  #日志输出路径
    LOG_PATH = "../test_logs/runlog_.text"  #打印日志等级
    LOG_LEVEL = "ERROR"  #测试报告输出路径
    REPORT_PATH = "../test_report/report.html"

2 yaml を使用して構成ファイルを作成する

メリットとデメリットは上記と同じなので、実際に例を見てみましょう。

#使用yaml作为配置文件,比较通用,跨语言有优势
#用例存储文件路径,及sheet页名
#建议采用相对路径

case_data_workbook:
  workbook: "../data_of_cases/case2.xlsx"
  worksheet: "Sheet1"

#log日志输出位置
#打印日志的等级
#日志切分   可按时间切分,也可按大小,如 1 MB切分

log_path: "../test_logs/runlog.log"
log_level: "ERROR"
log_rotation: "12:00"

#测试报告输出位置

report_path: "../test_report/report.html"

#后台域名,结合环境
host: "http://www.xxx.com:8108"
#前台域名
front_host: "http://www.xxx.com:8107"

3 テストプロセス中の要件

  • フレーム
  • リクエスト方法
  • ユースケースデータの読み取り
  • 環境構成の読み取り
  • データの前処理とクリーニング
  • 実行結果記録
  • テストレポート出力
  • ...

3. 自動化フレームワークに必要なモジュールやパッケージ、技術的なポイントを書く

1フレーム:

単体テスト

利点: Python が付属しており、使いやすいです。

デメリット:pytestに比べてカスタマイズの自由度が低い

Unittest の使用法とルールについては、巨匠のブログ Unittest - Duanlang Kuangdao Reminiscing about Youth - Blog Garden (cnblogs.com)を参照してください。

パイテスト

利点: カスタマイズの自由度が高く、単体テストに対応しており、デコレータが多数提供されている

デメリット:使用するにはダウンロードが必要

pytestの使い方やルールについては、師匠のブログ「  pytest - 青春を思い出すドゥアンランの狂剣 - Blog Garden (cnblogs.com)」を参照してください。

2リクエストメソッドリクエスト

3 ユースケースデータを読む openpyxl  python-operate openpyxl を Excel で読む - Duanlang Kuangdao が青春時代を思い出す - Blog Park (cnblogs.com)

4 環境設定 yaml/python ファイルを読み取ります。詳細については、前の記事を参照してください。

5ロギングログル/ロギング

ロギング

利点: 独自のニーズに完全に応じて関数を作成できます。

短所: 機能は基本的なものであり、開発が必要な領域が多くあります。

ロギングの使用方法と内容については、マスターのブログPython ロギング モジュール (詳細な分析)_Tianjian Humalingyueniao のブログ - CSDN ブログを参照してください。

利点: 高度なパッケージ化、安心

短所: 与えられたものは何でも使用できますが、すでにニーズを満たしています。

loguru の使い方や内容については、師匠のブログLoguru - 最も強力な Python ロガー - Python 実践ガイド (pythondict.com)を参照してください。

6MySQL データ処理

これについては何も言うことはありません。pymysql は使いやすいです。注意する必要があるのはデータベースのトランザクション conn.commit() です。

基本的な使い方

  • 1データベースに接続する
    • conn = pymysql.connect() は接続オブジェクトを取得します
  • 2 カーソルを取得する
    • カーソル = conn.cursor()
  • 3SQL文を使用する
    • カーソル.excute(SQL文)
  • 4クエリ結果の取得
    • Cursor.fetchall()、、、タプル内のタプル、データの一部は要素です
    • Cursor.fetchone(),,, タプルを取り出す
  • 各クエリの前にカーソルを初期化する
    • connect のcursorclass パラメータは、conn.cursor のcursor パラメータと同等であり、ネストされた辞書構造データのリストを読み取ります。
    • conn = pymysql.connect(cursorclass=pymysql.cursors.DictCursor)[画像のアップロードに失敗しました...(image-d27d50-1686663646198)]

pymysql - Duanlang Kuangdao が青春時代を思い出す - Blog Park (cnblogs.com)

7 テストレポート

単体テストレポート

Unittest  1 に適用されます。インストールと導入 - UnittestReport の使用ドキュメント

魅力

pytest に適用可能 Pytest テスト フレームワーク (5): pytest + allure でテスト レポートを生成 - テスト開発ノート - Blog Garden (cnblogs.com)

8つの技術ポイント

Python 関数、オブジェクト指向、継承、リフレクション、JSON、正規表現、カプセル化など、SQL 構文、単体テスト フレームワーク、リクエスト、デコレータなど。

4. フレームワークのディレクトリ構造

プロジェクト全体のディレクトリ構造を見てみましょう

Unittest フレームワークの Excel テーブルを使用してユースケースの yaml 構成ファイルを管理する

├─common                             #公共包
│ │ tool_of_log.py                     #日志模块
│ │ tool_of_mysql.py                   #数据库模块
│ │ tool_of_read_config.py             #读取配置模块
│ │ tool_of_read_excel.py              #表格数据处理
│ │ tool_of_requests.py                #通用请求方法
│ │ __init__.py
│ 
├─data_of_cases                     #用例数据包
│ │ case1.xlsx                           #用例表格
│ │ case2.xlsx
│
├─data_of_config                    #配置文件包
│ │ config.py                            #python类型的配置文件
│ │ config.yaml                          #yaml类型配置文件
│
├─run                               #测试框架主入口
│ │ main.py                             #主入口
│ │ __init__.py
│
├─setup                 #前置处理包,一些前置处理可以单抽出来,如获取cookie
│ │ 000345.png
│ │ new_account.py
│ │ put_product.py
│ │ uuid_imagecode.py
│ │ __init__.py
│
├─test_logs                               #测试日志包
│ │ runlog.log                              #测试日志文件
│
├─test_mainclass                          #测试主类
│ │ test_cla_and_meth.py                    #被测类和被测方法
│ │ test_new_account.py                      #场景测试 新增用户
│ │ test_product_upload.py                  #场景测试 上传产品
│ │ __init__.py
│
└─test_report                                #测试报告
│ │history.json                                  #测试报告相关json文件,自动生成的
│ │report_2022-09-06_23-57-26.html               #测试报告文件

5. コード例

一般

ログモジュール

"""
记录日志模块
sink为日志输出目录
level为写入日志等级
建议日志使用log文件记录
"""

from loguru import logger
from common.tool_of_read_config import read_config_yaml

# 这里是读取配置文件的日志输出位置
config = read_config_yaml("../data_of_config/config.yaml")
logger.add(sink=config["log_path"], level="ERROR", encoding="utf-8")

データベースモジュール

"""
连接数据库模块
提供了三个方法
1游标的初始化
2执行查询sql语句并销毁游标,返回查询到的数据
3关闭数据库连接
"""
import pymysql


class Tool_of_mysql:

    def __init__(self, host, port, user, password, dbname):
        # connect中的cursorclass参数,相当于conn.cursor中的cursor参数,读出一个列表嵌套字典结构数据
        self.conn = pymysql.connect(host=host, port=port, user=user, password=password, db=dbname,cursorclass=pymysql.cursors.DictCursor)
        self.cursor = self.conn.cursor()

    def cursor_init(self):
        # 执行一次SQL,游标会销毁,所以提供游标初始化方法
        self.cursor = self.conn.cursor()

    def run_sql(self, sql):
        # 这里建议只使用select语句
        self.cursor.execute(sql)
        # 提交事务
        self.conn.commit()

        db_data = self.cursor.fetchone()

        self.cursor.close()
        # self.conn.close()
        return db_data

    def mysql_close(self):
        self.conn.close()


if __name__ == '__main__':
    mon = Tool_of_mysql(host="47.113.xxx.81", port=3306, user="mon", password="mon123", dbname="yami_sops")
    # mon.cursor_init()

    data = lemon.run_sql("select user_phone,mobile_code from tz_sms_log limit 5")
    print(data)

構成ファイルモジュールの読み取り

"""
读取yaml配置文件模块
"""
import yaml


def read_config_yaml(filepath):
    with open(filepath, encoding="utf-8") as f:
        config_data = yaml.safe_load(f)
    return config_data


if __name__ == '__main__':
    print(read_config_yaml("../data_of_config/config.yaml"))

テーブルデータ処理モジュール

"""
读取excel表格的模块
"""

import openpyxl
from openpyxl.worksheet.worksheet import Worksheet


def read_xlsx_tool(filename, sheetname):
    workbook = openpyxl.load_workbook(filename)
    # 这里做了类型注解,方便ide给出代码联想提示
    sheet: Worksheet = workbook[sheetname]

    data = list(sheet.values)

    title = data[0]

    lines = data[1:]

    cases = [dict(zip(title, row)) for row in lines] #循环 并通过zip组装字段名和字段值并放进一个dict里
    # for row in lines:
    #     cases.append(dict(zip(title, row)))

    return cases

if __name__ == '__main__':

    s = read_xlsx_tool("../data_of_cases/case2.xlsx", "Sheet2")
    print(type(s))
    print(s[0])
    print(type(s[0]))

一般的なリクエスト方法。使用できない場合があります

"""
封装了请求方法的模块
默认请求方法post
其实该模块可以不封装,直接写代码并不是很多,处理比较方便,封装后加了一层导入
"""


import requests


def request_api(method='post', url=None, headers=None, json_data=None, body_data=None):
    res = requests.request(method=method, url=url, headers=headers, json=json_data, data=body_data)

    return res

構成のデータ

yaml環境設定ファイル

#使用yaml作为配置文件,比较通用,跨语言有优势
#用例存储文件路径,及sheet页名
#建议采用相对路径

case_data_workbook:
  workbook: "../data_of_cases/case2.xlsx"
  worksheet: "Sheet1"

#log日志输出位置
#打印日志的等级
#日志切分   可按时间切分,也可按大小,如 1 MB切分

log_path: "../test_logs/runlog.log"
log_level: "ERROR"
log_rotation: "12:00"

#测试报告输出位置

report_path: "../test_report/report.html"

#后台域名,结合环境
host: "http://www.xxx.com:8108"
#前台域名
front_host: "http://www.xxx.com:8107"

走る

テストフレームワークのメインエントランス

"""
运行测试的主模块,同时生成日志,和测试报告
"""
import unittest
import time
import unittestreport

suit = unittest.defaultTestLoader.discover("../test_mainclass")

runner = unittestreport.TestRunner(suit,
                                   filename=f"report_{time.strftime('%Y-%m-%d_%H-%M-%S')}.html",
                                   report_dir="../test_report",
                                   title="迭代10_接口自动化测试报告",
                                   tester="woody",
                                   desc="XX项目迭代10------",
                                   templates=2)
#文件名拼接时间,每次运行都会生成一个报告文件
runner.run()

テストログ

生成されたテストログ

プラグイン ideolog をインストールすることをお勧めします。pycharm はログを直接強調表示できます。

2022-09-06 23:57:26.096 | ERROR    | test_cla_and_meth:test_:36 - 用例---case_004--->执行失败   ====>{'code': 200, 'message': '登录成功', 'token': 'good-token'} != {'code': 200, 'message': '登录失败', 'token': 'none'}
- {'code': 200, 'message': '登录成功', 'token': 'good-token'}
?                             ^^             ^ ^^^^^^ -

+ {'code': 200, 'message': '登录失败', 'token': 'none'}
?                             ^^             ^ ^

2022-09-06 23:57:26.107 | ERROR    | test_cla_and_meth:test_:36 - 用例---case_005--->执行失败   ====>{'code': 200, 'message': '登录成功', 'token': 'good-token'} != {'code': 200, 'message': '登录失败', 'token': 'none'}
- {'code': 200, 'message': '登录成功', 'token': 'good-token'}
?                             ^^             ^ ^^^^^^ -

+ {'code': 200, 'message': '登录失败', 'token': 'none'}
?                             ^^             ^ ^

2022-09-19 23:53:30.900 | ERROR    | test_cla_and_meth:test_:39 - 用例---case_001--->执行失败   ====>'{"code":200,"message":"登录成功","token":"good-token"}' != <Request [post]>

テストメインクラス

テストされたクラスとテストされたメソッド

"""
编写测试类和测试方法
使用unittest框架,导入ddt和list_data两个装饰器,分别装饰测试类和测试方法
导入公共层表格读取模块,读取用例数据
导入公共层配置文件读取模块,读取配置文件
导入公共层日志记录器
导入被测方法/函数
导入json模块处理数据
"""

import unittest

import requests
from unittestreport import ddt, list_data  #特别注意!使用的ddt list_ddt是unittestreport这个模块提供的,不是我们常说的ddt模块 这里看五、数据驱动的使用 - unittestreport 使用文档 功能十分强大!

from common.tool_of_read_config import read_config_yaml
from common.tool_of_read_excel import read_xlsx_tool
from common.tool_of_log import logger
import json

config = read_config_yaml("../data_of_config/config.yaml")
host = config["host"]
cases = read_xlsx_tool(filename=config["case_data_workbook"]["workbook"],
                       sheetname=config["case_data_workbook"]["worksheet"])


# cases = read_xlsx_tool()

@ddt
class Test_cases(unittest.TestCase):
    @list_data(cases[0:1])  # 这里必须传入一个列表,所以定位单个用例,不能直接用索引,要用切片[{}],切出一个只有一个元素的列表
    def test_(self, case):
        # 注意这里的“json=“参数,表格读出来是字符串,要转成Json才能做请求入参
        # 这里的url是拼接的
        response = requests.request(method=case["method"], url=host + case["url"], json=json.loads(case["入参"]))

        # 这里使用try尝试取结果,返回类型不同使用不同的方法
        try:
            act_res = response.json()
        except Exception as e:

            res = response.text
            act_res = {"msg": res}

        expect = json.loads(case["预期结果"])

        # try进行断言是为了写日志,再手动抛出异常
        for k, v in expect.items():
            try:
                self.assertEqual(v, act_res[k])
            except AssertionError as e:
                logger.error(f'{e}')
                raise e
            # self.assertIn(case["预期结果"], res)
        # print(res)
        # expect_res = json.loads(case["预期结果"])  # 转成字典
        # try:
        #
        #     self.assertEqual(expect_res, res)
        # except AssertionError as e:
        #     # logger.error(f"用例---{case['用例名']}--->执行失败")
        #     logger.error(f"用例---{case['用例名']}--->执行失败   ====>{e}")
        #     raise e

リフレクションと正規表現マッチングを使用してビジネス シナリオをテストする

"""
编写测试类和测试方法
使用unittest框架,导入ddt和list_data两个装饰器,分别装饰测试类和测试方法
导入公共层表格读取模块,读取用例数据
导入公共层配置文件读取模块,读取配置文件
导入公共层日志记录器
导入被测方法/函数
导入json模块处理数据
"""
import re
import unittest

import requests
from unittestreport import ddt, list_data

from common.tool_of_read_config import read_config_yaml
from common.tool_of_read_excel import read_xlsx_tool
from common.tool_of_log import logger
import json
import time
from setup.put_product import login, img_put

config = read_config_yaml("../data_of_config/config.yaml")
host = config["host"]
cases = read_xlsx_tool(filename=config["case_data_workbook"]["workbook"],
                       sheetname="Sheet2")


# cases = read_xlsx_tool()

@ddt
class Test_cases(unittest.TestCase):
    def setUp(self) -> None:
        self.token = login()['access_token']
        self.img_id = img_put(self.token, "../setup/000345.png")
        print(self.token, self.img_id)
        self.time = f"{time.time()}"

    @list_data(cases[0:1])  # 这里必须传入一个列表,所以定位单个用例,不能直接用索引,要用切片[{}],切出一个只有一个元素的列表
    def test_(self, case):

        data = ""
        header = {"Authorization": f"bearer{self.token}"}
        # 法1:这里先使用replace替换,注意,相同的,一行代码全部会替换掉
        # data = case["入参"].replace("#time#", f"{time.time()}")
        # data = case["入参"].replace("#img_id#", self.img_id)

        # 法2:使用正则表达式替换,搜索出用#——#标记的字段,切掉#后,用反射读取类属性,然后使用replace方法替换
        flag = re.finditer("#(.+?)#", case["入参"])# 正则搜索匹配后返回一个迭代器 ,元素是"#___#"
        for i in flag:
            old = i.group()  # 用group方法从迭代器取值
            old_ = old.strip("#") # 切掉两头的#号,剩下的就是"token",这种用例入参里的某个键
            new = getattr(self, old_)# 使用反射,getattr拿到属性的值并用变量保存起来
            data = case["入参"].replace(old_, new)# 使用replace方法把属性再替换到入参字符串里
        # 注意这里的“json=“参数,表格读出来是字符串,要转成字典才能做请求入参
        data = json.loads(data)

        # 这里的url是拼接的
        response = requests.request(method=case["method"], url=host + case["url"], json=data, headers=header)

        # response = product_put(self.token,self.img_id)

        # 这里使用try尝试取结果,返回类型不同使用不同的方法
        try:
            act_res = response.json()
        except Exception as e:

            res = response.text
            act_res = {"msg": res}
        print(act_res)
        expect = json.loads(case["预期结果"])

        # try进行断言是为了写日志,再手动抛出异常
        for k, v in expect.items():
            try:
                self.assertEqual(v, act_res[k])
            except AssertionError as e:
                logger.error(f'{e}')
                raise e
            # self.assertIn(case["预期结果"], res)
        # print(res)
        # expect_res = json.loads(case["预期结果"])  # 转成字典
        # try:
        #
        #     self.assertEqual(expect_res, res)
        # except AssertionError as e:
        #     # logger.error(f"用例---{case['用例名']}--->执行失败")
        #     logger.error(f"用例---{case['用例名']}--->执行失败   ====>{e}")
        #     raise e

インターフェース自動化におけるリフレクションの適用についての考え

前のコードは、object."attribute" を文字列で調整できないため、リフレクションが非常に便利であることを示しています。そのため、リフレクションを使用する必要があります。hasattr は、リフレクションがあるかどうかを判断し、getattr は属性値を取得できます。メソッドの場合は、 、変数を保存した後、() を追加してそれを呼び出し、setattr を使用して新しい属性を設定します。

UI オートメーションにリフレクションを取り入れたらどうなるでしょうか?

クリックや入力などの高頻度の UI 操作をカプセル化します。

class Action:    def __init__():  '''  初始化浏览器对象等  '''  def mouse_click(元素定位: string):
        '''
        例,将鼠标点击的操作,封装,传入元素定位,如id或者xpath,进行鼠标点击
        '''
        pass
    
    def keyboard_input(元素定位: string ,输入内容: string ):     '''     键盘输入操作需要定位元素并有输入的内容     先定位  v[0]     输入    v[1]     '''      
        pass

渡す入力パラメータの形式は次のとおりです。

{
"mouse_click":元素定位,
"keyboard_input":[元素定位,输入内容],
...
}

ui 操作を json に整理して渡し、ループを通じて ui を取得して操作します。

action = Action()for k,v in case['ui操作'].items:
    move = getattr(action,k)   if type(v) is list:
      move(*v)  # 调用解包,传入一个序列打散后当位置参数传进去  else:     move(v)

私は UI 自動化についてはほとんど研究を行っていませんが、Playwright を参照することをお勧めします。

Python Playwright の基本的な使用法 (詳細な手順) - Nuggets (juejin.cn)

インストール | 劇作家パイソン

Python はどのようにクロールするのでしょうか? 新世代のクローラー アーティファクト Playwright で遊んでみましょう! - 志湖(zhihu.com)

Selenium と比較して、Playwright は実行効率が良く、強力なスクリプト記録機能があり、コードの難易度が低いです。

試験報告書

テストレポート

最後に: 以下はサポート学習資料です。[ソフトウェア テスト] を行っている人にとって、これは最も包括的で完全な準備倉庫となるはずです。この倉庫は、最も困難な旅にも同行してくれました。また、お役に立てれば幸いです。【騙しなしで100%無料で受け取れます】

ソフトウェアテストインタビューアプレット

何百万人もの人々が使用しているソフトウェア テストの質問バンクです。誰が知っているのか!インターネット上で最も包括的な面接テスト ミニ プログラムです。携帯電話を使用して質問に答えたり、地下鉄やバスに乗ったり、試験に参加したりすることができます。

次のインタビューの質問セクションをカバーします。

1. ソフトウェアテストの基礎理論、2. Web、アプリ、インターフェース機能テスト、3. ネットワーク、4. データベース、5. Linux

6. Web、アプリ、インターフェイスの自動化、7. パフォーマンス テスト、8. プログラミングの基本、9. 人事面接の質問、10. 公開テストの質問、11. セキュリティ テスト、12. コンピューターの基本

  完全な情報を入手する方法: 下の小さなカードをクリックしてご自身で入手してください

おすすめ

転載: blog.csdn.net/weixin_57794111/article/details/133390447