[自動テスト] Pytest+Appium+Allure で UI 自動化を実現

本文では主に、Pytest+Allure+Appium によって記録されたいくつかのプロセスと経験を紹介します。

法律は主に何を使用しますか?

Python3
Appium
Allure-pytest
Pytest
Appium 珍しいが使いやすいメソッド
Appium は adb シェル メソッドを直接実行します
# Appium が adb シェルと同様のメソッドの実行を開始するときに、--relaxed-security パラメータ Appium を追加します
> appium -p 4723 --relaxed-安全

# メソッド
def adb_shell(self, command, args, includeStderr=False) を使用します:
"""
appium --relaxed-security
adb_shell('ps',['|','grep','android']) を起動する方法

:param command:command
:param args:parameter
:param includeStderr: True の場合、例外がスローされます
:return:
"""
result = self.driver.execute_script('mobile: Shell', { 'command': command , 'args': args, 'includeStderr': includeStderr, 'timeout': 5000 }) return result['stdout']





要素画像を直接インターセプトする Appium の方法
element = self.driver.find_element_by_id('cn.xxxxxx:id/login_sign')
pngbyte = element.screenshot_as_png
image_data = BytesIO(pngbyte)
img = Image.open(image_data)
img.save('element .png')
# このメソッドはログインボタン領域のスクリーンショットを直接取得できます

Appium は携帯電話上のログを直接取得します
# この方法を使用すると、携帯電話上の logcat キャッシュがクリアされてゼロにリセットされ、再度記録されます
# 各ユースケースの実行後にクリーンアップして保存することをお勧めしますエラーが発生した場合の古いログ出力を削減します
# Android
logcat = self.driver.get_log('logcat')

# iOS は brew install libimobiledevice をインストールする必要があります
logcat = self.driver.get_log('syslog')

# Web コンソールログを取得
logcat = self.driver.get_log('browser')

c = '\n'.join([i['message'] for i in logcat])
allure.attach(c, 'APPlog', allure.attachment_type.TEXT)
#allure テスト レポートに書き込む

Appium はファイルをデバイスに直接転送します
# ファイルを送信
#Android
driver.push_file('/sdcard/element.png', source_path='D:\works\element.png')

# 電話ファイルを取得します
png = driver.pull_file('/sdcard/element.png')
with open('element.png', 'wb') as png1:
png1.write(base64.b64decode(png))

# 携帯電話のフォルダーを取得し、zip ファイルをエクスポート

ます
。 ))

# iOS
# ifuseをインストールする必要がある
# > brew install ifuse または > brew cask install osxfuse またはインストール方法を自分で探す

driver.push_file('/Documents/xx/element.png',source_path='D:\works\element.png')

# ファイルをアプリのサンドボックスに送信します
# iOS 8.3 以降、アプリケーションは UIFileSharingEnabled 権限を有効にする必要があります。有効にしないとエラーが報告されます
BundleId = 'cn.xxx.xxx' # APP 名
driver.push_file('@{bundleId}/Documents /xx/element.png' .format(bundleId=bundleId)、source_path='D:\works\element.png')

Pytest と Unittest の初期化の違い
多くの人が Unitest を使用していると思いますが、Hook メソッドにおける pytest と Unittest の違いについてお話します。

1. Pytest は Unitest に似ていますが、いくつかの違いがあります。以下は Pytest
クラス TestExample:
def setup(self):
print("setup class:TestStuff")です。

def ティアダウン(自己):
print ("ティアダウン クラス:TestStuff")

def setup_class(cls):
print ("setup_class クラス:%s" % cls.__name__)

def teadown_class(cls):
print ("teardown_class クラス:%s" % cls.__name__)

def setup_method(self, method):
print ("setup_method メソッド:%s" % method.__name__)

def teadown_method(self, method):
print ("teardown_method メソッド:%s" % method.__name__)

2. pytest.fixture()
@pytest.fixture()を使用します
def driver_setup(request):
request.instance.Action = DriverClient().init_driver('android')
def driver_teardown():
request.instance.Action.quit()
request.addfinalizer(driver_teardown)

インスタンス 1.setup_class メソッドを初期化してクラス Singleton(object)
を呼び出します: """Singleton ElementActions はそれ自体の操作クラスをカプセル化します""" Action = None



def __new__(cls, *args, **kw):
hasattr(cls, '_instance') でない場合:
desired_caps={}
host = "http://localhost:4723/wd/hub"
driver = webdriver.Remote(host 、desired_caps)
アクション = ElementActions(driver,desired_caps)
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls, *args, **kw)
cls._instance.Action = アクション
return cls._instance

クラス DriverClient(Singleton):
パス

テストケースで呼び出される

クラス TestExample:
def setup_class(cls):
cls.Action = DriverClient().Action

def ティアダウンクラス(cls):
cls.Action.clear()


def test_demo(self)
self.Action.driver.launch_app()
self.Action.set_text('123')

2. pytest.fixture() は
クラス DriverClient() を呼び出します。

def init_driver(self,device_name):
desired_caps={}
host = "http://localhost:4723/wd/hub"
driver = webdriver.Remote(host,desired_caps)
Action = ElementActions(driver,desired_caps)
return アクション


# この関数は conftest.py に配置する必要があり、pytest は@pytest.fixture()
def driver_setup(request):
request.instance.Action = DriverClient().init_driver()
def driver_teardown(): requestを自動的に取得します
。インスタンス。Action.clear()
request.addfinalizer(driver_teardown)

テストケースで呼び出される

#デコレーターは、driver_setup 関数
@pytest.mark.usefixtures('driver_setup')
クラス TestExample を直接導入します。

def test_demo(self):
self.Action.driver.launch_app()
self.Action.set_text('123')

Pytestのパラメータ化メソッド
1.最初のメソッドparametrizeデコレータのパラメータ化メソッド
@pytest.mark.parametrize(('kewords'), [(u"Xiaoming"), (u"Xiaohong"), (u"Xiaobi ")]) def
test_kewords (self,キーワード):
print(キーワード)

# 複数パラメータ
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(test_input, Expected):
assert eval(test_input) == Expected

2. 2 番目の方法は、pytest フックを使用してパラメータ化をバッチで追加することです。#
conftest.py
def pytest_generate_tests(metafunc):
"""
フックを使用して、
クラス

"""
試してみてください:
metafunc.cls.params と metafunc.function.__name__ の場合: ## 对应 TestClass params
funcarglist = metafunc.cls.params[metafunc.function.__name__]
argnames = list(funcarglist[0] ])
metafunc.parametrize(argnames, [[funcargs[name] for name in argnames] for funcargs in funcarglist])
AttributeError を除く:
pass

# test_demo.py
class TestClass:
"""
:params 对应フック中 metafunc.cls.params
"""
# params = Parameterize('TestClass.yaml').getdata()

params = { 'test_a': [{'a': 1, 'b': 2}, {'a': 1, 'b': 2}], 'test_b': [{'a': 1, ' b': 2}, {'a': 1, 'b': 2}], } def test_a(self, a, b): a == b をアサートdef test_b(self, a, b): a = をアサート= b






Pytest ユース ケースの依存関係
pytest-dependency ライブラリを使用して依存関係を作成する
上位レベルのユース ケースが失敗した場合、後続の依存関係のユース ケースは直接スキップされ、クラス全体でフィルター処理できます
。 site-packages/pytest_dependency.py ファイルをコピーするには

class dependencyManager(object):
"""依存関係マネージャー。テストの結果を保存します。
"""

ScopeCls = {'モジュール':pytest.Module, 'セッション':pytest.Session}

@classmethod
def getManager(cls, item,scope='session'): # ここでセッションに変更します

もしも

> pip インストール pytest-dependency

クラス TestExample(オブジェクト):

@pytest.mark.dependency()
def test_a(self):
False をアサートします

@pytest.mark.dependency()
def test_b(self):
False をアサートします

@pytest.mark.dependency(depends=["TestExample::test_a"])
def test_c(self):
# TestExample::test_a が失敗した場合、ユースケースは実行されません
# クラス間でフィルタリングできます
print("Hello I am test_c 内 ")

@pytest.mark.dependency(depends=["TestExample::test_a","TestExample::test_b"])
def test_d(self):
print("こんにちは、test_d です")

pytest -v test_demo.py
2 が失敗しました
- test_1.py:6 TestExample.test_a
- test_1.py:10 TestExample.test_b
2 がスキップされました

ユースケースのスクリーニングを実行するための Pytest カスタム マーク
1. @pytest.mark モジュールを使用して、ユースケースの実行時にスクリーニングするクラスまたは関数をマークします
@pytest.mark.webtest
def test_webtest():
pass


@pytest.mark.apitest
クラス TestExample(object):
def test_a(self):
pass

@pytest.mark.httptest
def test_b(self):
パス

webtest とマークされたユースケースのみを実行します

pytest -v -m ウェブテスト

結果 (0.03 秒):
1 件が合格、
2 件が選択解除

実行マークの複数の使用例

pytest -v -m "webtest または apitest"

結果 (0.05 秒):
3 名が合格

マークされた Web テストを実行しないユースケースのみ

pytest -v -m "Webテストではありません"

結果 (0.04 秒):
2 件が合格、
1 件が選択解除

実行せずに複数のユースケースをマークする

pytest -v -m "Webtest でも apitest でもありません"

結果 (0.02 秒):
3 件が選択解除されました

2. テスト ノードに応じてユース ケースを選択します。 pytest
-v Test_example.py::TestClass::test_a
pytest -v Test_example.py::TestClass
pytest -v Test_example.py Test_example2.py

3. pytest フックを使用してバッチ内のユースケースをマークします
# conftet.py

def pytest_collection_modifyitems(items):
"""
各関数の名前を取得し、その文字がユースケースに含まれる場合にマークします。items 内の項目の
""" : if "http" in item.nodeid: item.add_marker(pytest .mark.http ) item.nodeid の elif "api": item.add_marker(pytest.mark.api)




クラス TestExample(object):
def test_api_1(self):
pass

def test_api_2(self):
パス

def test_http_1(self):
パス

def test_http_2(self):
パス
def test_demo(self):
パス

タグ付けされた API のユースケースのみを実行する

pytest -v -m api
結​​果 (0.03 秒):
2 が合格
3 が選択解除
バッチ マークが使用された後、API を使用したメソッドのみがテスト ケースで実行されることがわかります。

スクリーンショット、アプリログなどのエラー処理のユースケース
1. Python 関数デコレーターを使用した最初の方法

def Monitorapp(function):
"""
ケースデコレーター、スクリーンショット、ログ、スキップするかどうかなどを使用します。
システムログを取得します。Android logcat、ios は syslog を使用します
"""

@wraps(function)
def Wrapper(self, *args, **kwargs):
try:
allure.dynamic.description('ユースケース開始時刻: {}'.format(datetime.datetime.now()))
function(self , *args, **kwargs)
self.Action.driver.get_log('logcat')
E としての例外を除く:
f = self.Action.driver.get_screenshot_as_png()
allure.attach(f, '失敗したスクリーンショット', allure.attachment_type .PNG)
logcat = self.Action.driver.get_log('logcat')
c = '\n'.join([i['message'] for i in logcat])
allure.attach(c, 'APPlog', allure .attachment_type.TEXT)
raise
Efinally:
if self.Action.get_app_pid() != self.Action.Apppid:
raise Exception('デバイス プロセス ID が変更されると、クラッシュが発生する可能性があります')
return Wrapper

2. pytest フックを使用する 2 番目の方法 (どちらかを選択)

@pytest.hookimpl(tryfirst=True,hookwrapper=True)
def pytest_runtest_makereport(item, call):
Action = DriverClient().Action
結果 = yield
rep = result.get_result()
if rep.when == "call" および rep.失敗しました:
f = Action.driver.get_screenshot_as_png()
allure.attach(f, '失敗したスクリーンショット', allure.attachment_type.PNG)
logcat = Action.driver.get_log('logcat')
c = '\n'.join([ i['message'] for i in logcat])
allure.attach(c, 'APPlog', allure.attachment_type.TEXT)
if Action.get_app_pid() != Action.apppid:
raise Exception('デバイス プロセス ID が変更される可能性がありますクラッシュが発生しました')

Pytest で他のフックを使用する方法
1. Pytest パラメータをカスタマイズ
> pytest -s -all

# conftest.py の内容
def pytest_addoption(parser):
"""
自定パラメータ
"""
parser.addoption("--all", action="store_true",default="type1",help="すべての組み合わせを実行する" )

def pytest_generate_tests(metafunc):
if 'param' in metafunc.fixturenames:
if metafunc.config.option.all: # ここでカスタムパラメータを取得できます
paramlist = [1,2,3]
else:
paramlist = [1,2, 4 ]
metafunc.parametrize("param",paramlist) # ユースケースにパラメータ化を追加します

# テスト ケースでカスタム パラメーターを取得するには?
# conftest.py の内容
def pytest_addoption(parser):
"""
カスタム パラメーター
"""
parser.addoption("--cmdopt", action="store_true",default= "type1 ",help="すべての組み合わせを実行")


@pytest.fixture
def cmdopt(request):
return request.config.getoption("--cmdopt")


# test_sample.py
def test_sample(cmdopt):
if cmdopt == "type1":
print("first")
elif cmdopt == "type2":
print("first")
アサート 1

> pytest -q --cmdopt=type2


1 は 0.09 秒で通過

2. Pytest フィルター テスト ディレクトリ
#pytest が実行する必要があるフォルダーまたはファイル名をフィルターします。
def pytest_ignore_collect(path,config):
path.dirname に 'logcat' がある場合:
return True #Return True、ファイルは実行されません

Pytest の一般的なメソッド
Pytest ユースケースの優先順位 (優先ログインなど)
> pip install pytest-ordering

@pytest.mark.run(order=1)
クラス TestExample:
def test_a(self):

Pytest ユースケースの失敗の再試行
#元のメソッド
pytet -s test_demo.py
pytet -s --lf test_demo.py #2 回目の実行時は、失敗したユースケースのみが実行されます
pytet -s --ll test_demo.py #2 回目の実行、すべてのユースケースが実行されますが、失敗したケースが最初に実行されます
#サードパーティのプラグインを使用します
pip install pytest-rerunfailures #プラグイン
pytest --reruns 2 を使用します #失敗したケースは 2 回再試行します

Pytest のその他の共通パラメータ
pytest --maxfail=10 #失敗が 10 回を超えた場合は pytest の実行を停止
pytest -x test_demo.py #失敗が発生した場合は停止

学習の段取りとしては、
現地に行った者として、皆さんも遠回りは避けていただきたいと思います。数日間粘り強く続けた後、ここでいくつかの自動化を共有します。テストの学習リソースは、その過程で役立つことを願っています。【完全無料保証】

動画ファイルの入手方法:

このドキュメントとビデオ資料は、[ソフトウェア テスト] に参加したい友人にとって、最も包括的で完全な準備倉庫となるはずです。この倉庫は、最も困難な旅を私に同行させてくれたものでもあり、あなたにも役立つことを願っています。上記はすべて共有することができ、下の小さなカードをクリックして自分で受け取ることもできます。

おすすめ

転載: blog.csdn.net/2301_76643199/article/details/131141930