コンテンツ
1.依存関係とプロジェクトのベースディレクトリをインストールします
私をクリックして、ソフトウェアテスト(自動テスト)ビデオ資料のフルセットを無料で入手してください(「csdn000」に注意)
1.依存関係とプロジェクトのベースディレクトリをインストールします
#インストールの依存関係
pip install pytest
pip install appium-python-client
pip install openpyxl#Excelファイル処理
pip install pytest-html#テストレポート
2.pom解析
pomデザインの中心的なアイデアは、異なるページを個別に維持することです。自動化の過程で、フロントエンドページが変更されると、フロントエンドページが変更された後、元の自動化コードが適用されなくなる可能性があります。要素の配置が適切でなくなったため、自動化のユースケースの実行に失敗しました。コードを再度変更するのはもっと面倒です。
pomはページとテストケースを別々にカプセル化し、ページ上の各操作は別々にカプセル化され、テストケースはカプセル化されたメソッドを呼び出すだけで済みます。ページが変更された場合。ページにカプセル化されている操作を変更するだけです。
以下は、自動化を作成するための例としてログインシナリオを取り上げています。
conftest.py
from appium import webdriver
import pytest
import os
chromedriver= os.path.join(os.path.dirname(os.path.abspath(__file__)),'drivers/chrome/75.0.3770.140/chromedriver.exe')
@pytest.fixture(scope='session')
def driver():
desired_caps = {
'platformName': 'Android', # 测试Android系统
'platformVersion': '7.1.2', # Android版本 可以在手机的设置中关于手机查看
'deviceName': '127.0.0.1:62001', # adb devices 命令查看 设置为自己的设备
'automationName': 'UiAutomator2', # 自动化引擎
'noReset': False, # 不要重置app的状态
'fullReset': False, # 不要清理app的缓存数据
'chromedriverExecutable': chromedriver, # chromedriver 对应的绝对路径
'appPackage': "org.cnodejs.android.md", # 应用的包名
'appActivity': ".ui.activity.LaunchActivity" # 应用的活动页名称(appium会启动app的加载页)
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities=desired_caps)
driver.implicitly_wait(5) # 全局的隐式等待时间
yield driver # 将driver 传递出来
driver.quit()
pom/basePage.py
"""
所有的页面都继承这个类,获得driver
"""
import time
from appium.webdriver.webdriver import WebDriver
from selenium.common.exceptions import NoSuchElementException
class BasePage:
def __init__(self,driver:WebDriver):
self.driver = driver
# 获取toast的文本值
@property
def result_text(self):
try:
toast = self.driver.find_element_by_xpath('//android.widget.Toast')
return toast.text
except NoSuchElementException:
return "找不到这个元素,请检查自己的自动化代码"
pom/loginPage.py
"""
登陆页面
"""
import time
from appium.webdriver.webdriver import WebDriver
from pom.basePage import BasePage
class LoginPage(BasePage):
# 初始化类的时候,打开登陆页面
def __init__(self,driver:WebDriver):
super(LoginPage,self).__init__(driver)
# 判断是否是登陆页面
current_activity = self.driver.current_activity
if ".ui.activity.LoginActivity" in current_activity:
pass
else:
# 不是登陆页面,则调用方法,打开登陆页面
self.__go_login_page()
# 导航到loginPage(登陆页面),定义一个私有的方法
def __go_login_page(self):
# 清空app的登陆状态(如果已经登陆,则去掉登陆状态)
self.driver.reset()
# 打开首页
self.driver.start_activity(app_package='org.cnodejs.android.md',app_activity='.ui.activity.MainActivity')
toggle_but = self.driver.find_element_by_android_uiautomator('resourceId("org.cnodejs.android.md:id/toolbar").childSelector(new UiSelector().className("android.widget.ImageButton"))')
toggle_but.click()
time.sleep(1)
# 点击头像,去登陆页面
avatar = self.driver.find_element_by_android_uiautomator('text("点击头像登录").resourceId("org.cnodejs.android.md:id/tv_login_name")')
avatar.click()
# 使用token的方式进行登录
def with_token_login(self,token):
self.driver.find_element_by_id('org.cnodejs.android.md:id/edt_access_token').send_keys(token)
loginbtn = self.driver.find_element_by_android_uiautomator('text("登录").resourceId("org.cnodejs.android.md:id/btn_login")')
# 点击登陆
loginbtn.click()
# 登陆失败的断言
@property
def with_token_failed_text(self):
# 1. 截图
ele = self.driver.find_element_by_id('org.cnodejs.android.md:id/edt_access_token')
png = ele.screenshot_as_base64
# 2. TODO 调用 ocr 图片识别 将图片中文字识别出来
return ""
# 扫码登陆
def with_code_login(self):
pass
# 使用github登陆
def with_github_login(self):
pass
testcases/test_user.py
from pom.loginPage import LoginPage
# 登陆的测试用例
# 使用conftest.py 中定义的 driver
def test_login(driver):
# 打开登录页面
loginpage = LoginPage(driver)
# 使用token进行登录
loginpage.with_token_login('d1563473-1f0d-4307-9774-6c2ff49c93ab')
# 登陆成功,验证totas文本值
result = loginpage.result_text
assert result == "登录成功"
appiumを起動し、 pytest testcases \ test_user.py -s -v を実行して、実行結果を確認します。
(着陸成功)
(ログインに失敗しました)
以下は、自動化を作成するための例として、ログイン後のシナリオを取り上げています。
pom/homePage.py
"""
首页
"""
from pom.basePage import BasePage
from pom.createTopicPage import CreateTopicPage
class HomePage(BasePage):
def __init__(self,driver):
super(HomePage,self).__init__(driver)
# 判断一下,是否是首页
if '.ui.activity.MainActivity' in self.driver.current_activity:
pass
else:
self.__go_home_page()
# 打开首页
def __go_home_page(self):
self.driver.start_activity(app_activity='.ui.activity.LaunchActivity')
# 去发帖页面
def go_create_topic(self):
# 判断是否已经到达创建话题页面
while not '.ui.activity.CreateTopicActivity' in self.driver.current_activity:
# 再重新点击一下
create_btn = self.driver.find_element_by_android_uiautomator(
'.resourceId("org.cnodejs.android.md:id/fab_create_topic")')
create_btn.click()
return CreateTopicPage(self.driver)
pom/createTopicPage.py
"""
发帖页面
"""
from pom.basePage import BasePage
class CreateTopicPage(BasePage):
# 发布话题
def create_new_topic(self,tab,title,content):
# 选择类型
spinner = self.driver.find_element_by_android_uiautomator('.resourceId("org.cnodejs.android.md:id/spn_tab")')
spinner.click()
tab_selcotor = f'.resourceId("android:id/text1").text("{tab}")'
self.driver.find_element_by_android_uiautomator(tab_selcotor).click()
# 输入标题
title_content = self.driver.find_element_by_android_uiautomator(
'resourceId("org.cnodejs.android.md:id/edt_title")')
title_content.send_keys(title)
# 输入内容
content_area = self.driver.find_element_by_android_uiautomator(
'resourceId("org.cnodejs.android.md:id/edt_content")')
content_area.send_keys(content)
# 点击发送
send_btn = self.driver.find_element_by_android_uiautomator(
'resourceId("org.cnodejs.android.md:id/action_send")')
send_btn.click()
testcases/test_topics.py
from pom.homePage import HomePage
from pom.loginPage import LoginPage
# 发帖的测试用例
def test_create_topic(driver):
loginpage = LoginPage(driver)
# 用户登录成功
loginpage.with_token_login('d1563473-1f0d-4307-9774-6c2ff49c93ab')
# 首页打开
hp = HomePage(driver)
# 进入创建话题页面
create_page = hp.go_create_topic()
create_page.create_new_topic(tab='分享',title='123',content='哈哈哈哈哈哈')
result = create_page.result_text
# 根据发帖结果做断言
assert result == "标题要求10字以上"
私をクリックして、ソフトウェアテスト(自動テスト)ビデオ資料のフルセットを無料で入手してください(「csdn000」に注意)
appiumを起動し、 pytest testcases \ test_topics.py -s -v を実行して、実行結果を表示します。
pytestを実行し、ログインを表示し、2つのテストケースの実行結果を投稿することもできます。
3.Excel データドリブン
testdata / data.xlsx
utils/file_handler.py
"""
登陆测试用例的数据驱动化测试
"""
import pytest
from pom.loginPage import LoginPage
from utils.file_handler import FileHandler
fl = FileHandler()
# 从Excel文件中获取数据
data = fl.get_data_by_sheet('用户登录')
class TestDdtLogin:
@pytest.mark.parametrize('token,status,expect_val',data)
def test_login(self,driver,token,status,expect_val):
# 打开登录页面
loginpage = LoginPage(driver)
# 使用token进行登录
loginpage.with_token_login(token)
if status == '成功':
# 登录成功, 验证toast的文本值为登录成功
result = loginpage.result_text
assert result == expect_val
if status == "失败":
result = loginpage.with_token_failed_text
assert result == expect_val
testcases/test_ddt/test_ddt_login.py
"""
登陆测试用例的数据驱动化测试
"""
import pytest
from pom.loginPage import LoginPage
from utils.file_handler import FileHandler
fl = FileHandler()
# 从Excel文件中获取数据
data = fl.get_data_by_sheet('用户登录')
class TestDdtLogin:
@pytest.mark.parametrize('token,status,expect_val',data)
def test_login(self,driver,token,status,expect_val):
# 打开登录页面
loginpage = LoginPage(driver)
# 使用token进行登录
loginpage.with_token_login(token)
if status == '成功':
# 登录成功, 验证toast的文本值为登录成功
result = loginpage.result_text
assert result == expect_val
if status == "失败":
result = loginpage.with_token_failed_text
assert result == expect_val
appiumを起動し、 pytest testcases \ test_ddt \ test_ddt_login.py -s -v を実行し、実行結果を確認します。
4.テストレポート
pytest-html pytest-html・PyPI
pytest-allure allure-pytest PyPI
テストレポート1:pytest-html
main.py
"""
项目运行文件,并添加测试报告
"""
import pytest
import os,time
if __name__ == '__main__':
report_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'reports')
if not os.path.exists(report_dir):
os.mkdir(report_dir)
report = time.strftime('%Y_%m_%d_%H_%M_%S')
reportfile = os.path.join(report_dir,report+'.html')
pytest.main(['testcases','-s','-v',f'--html={reportfile}'])
main.pyファイルを実行し、main.pyを実行して実行すると、テストケース内のすべてのテストケースが実行されます
実行が終了したら、生成されたテストレポートをブラウザで開くことができます
5.conftest.pyを最適化します
各テストケースが実行された後、実行でスクリーンショットが撮れなかった場合、スクリーンショットの名前はテストケース名+時間形式関連の処理になります
from appium import webdriver
import pytest
import os, time
chromedriver= os.path.join(os.path.dirname(os.path.abspath(__file__)),'drivers/chrome/75.0.3770.140/chromedriver.exe')
# scope='session' 标记的方法执行域为---->所有测试用例运行之前/之后 运行的方法
@pytest.fixture(scope='session',autouse=True)
def driver():
desired_caps = {
'platformName': 'Android', # 测试Android系统
'platformVersion': '7.1.2', # Android版本 可以在手机的设置中关于手机查看
'deviceName': '127.0.0.1:62001', # adb devices 命令查看 设置为自己的设备
'automationName': 'UiAutomator2', # 自动化引擎
'noReset': False, # 不要重置app的状态
'fullReset': False, # 不要清理app的缓存数据
'chromedriverExecutable': chromedriver, # chromedriver 对应的绝对路径
'appPackage': "org.cnodejs.android.md", # 应用的包名
'appActivity': ".ui.activity.LaunchActivity" # 应用的活动页名称(appium会启动app的加载页)
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities=desired_caps)
driver.implicitly_wait(5) # 全局的隐式等待时间
yield driver # 将driver 传递出来
driver.quit()
# 该方法是用来获取测试用例执行的结果(passed / FAILED)
@pytest.hookimpl(tryfirst=True,hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result() # 获取用例的执行结果
print('用例的执行结果rep---->',rep)
setattr(item, "rep_" + rep.when, rep) # 将执行结果保存到 item 属性中 , req.when 执行时
# scope='function' 标记的方法执行域为---->每个测试用例运行之前/之后 运行的方法
@pytest.fixture(scope='function',autouse=True)
def case_run(driver:webdriver,request): # request 为 pytest_runtest_makereport 方法获取到的执行结果(固定参数和用法)
yield
# 每个测试用例执行完成之后,如果执行失败截图,截图的名称为测试用例名称+时间格式
if request.node.rep_call.failed:
screenshots = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'screenshots')
if not os.path.exists(screenshots):
os.mkdir(screenshots)
casename: str = request.node.nodeid
# print("执行测试用例的名字:", casename)
# 测试用例的名字很长 testcases/test_ddt/test_ddt_login.py::TestDdtLogin::test_login[....]
# 对字符串进行截取,截取之后显示为 test_ddt_login-TestDdtLogin
casename = casename[casename.rfind('/')+1:casename.rfind('::')].replace('.py::','-')
filename = casename + '-' + time.strftime('%Y_%m_%d-%H_%M_%S') +".png"
screenshot_file = os.path.join(screenshots, filename)
# 保存截图
driver.save_screenshot(screenshot_file)
6.マルチデバイス接続、テストコードの並列実行
コードを介して接続された複数のデバイスを自動的に取得し、デバイスのシリアル番号を取得し、複数のappiumを起動し、マルチプロセス方式を使用して複数のデバイスで自動テストケースを並行して実行します
クリックして最適化されたコードを表示
最後に、pipfreeze> Requirements.txtを実行して、プロジェクトで使用されているサードパーティパッケージのパッケージ名とバージョンをファイルにエクスポートします。
pip install -r Requirements.txt#ライブラリと対応するバージョンをrequirements.txtフォルダーにインストールします
私をクリックして、ソフトウェアテスト(自動テスト)ビデオ資料のフルセットを無料で入手してください(「csdn000」に注意)