具体什么是PO模式就不用详细介绍了,web自动化测试已经介绍过了,详细内容可参考此篇文章中的4.3.2UI自动化测试介绍_晒不黑的黑煤球的博客-CSDN博客_ui自动化测试
我这里新建了三个模块home_page.py(首页),login_page.py(登录页),user_page.py(用户页),也可以把登录页的内容放到用户页面中,具体编写代码如下
home_page.py代码如下:
from pages.native_page import NativePage
class HomePage(NativePage):
# 点击我的柠檬
locator_my = ('id', 'com.lemon.lemonban:id/navigation_my')
def enter_userpage(self):
"""进入用户界面"""
self.click(self.locator_my)
login_page.py代码如下:
from pages.native_page import NativePage
class LoginPage(NativePage):
locator_mobile = ('id', 'com.lemon.lemonban:id/et_mobile')
locator_pwd = ('id', 'com.lemon.lemonban:id/et_password')
locator_login = ('id', 'com.lemon.lemonban:id/btn_login')
def login(self, mobile, password):
"""登录"""
self.type(self.locator_mobile, mobile)
self.type(self.locator_pwd, password)
self.click(self.locator_login)
def get_error_info(self):
"""获取登录失败错误信息"""
return self.get_toast_text()
user_page.py代码如下:
from pages.native_page import NativePage
class UserPage(NativePage):
# 点击我的头像
locator_avatar = ('id', 'com.lemon.lemonban:id/fragment_my_lemon_avatar_title')
def click_login(self):
"""点击登录头像"""
self.click(self.locator_avatar)
def get_uesrname(self):
"""获取登录后用户名称"""
return self.get_avatar_title()
native_page.py代码如下:(内容还是之前写的,我粘贴到一块了)
from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as when
class MobileKey:
ENTER = 66
VOLUME_UP = 24
VOLUME_DOWN = 25
BACK = 4
class NativePage:
def __init__(self, driver):
self.driver = driver
def click(self, locator):
"""点击"""
wait = WebDriverWait(self.driver, timeout=5)
condition = when.element_to_be_clickable(locator)
element = wait.until(condition)
ActionChains(self.driver).click(element).perform()
def type(self, locator, words):
"""输入"""
el = self.driver.find_element(*locator)
el.send_keys(words)
def get_toast_text(self):
"""获取错误信息"""
toast = self.driver.find_element('xpath', '//android.widget.Toast')
return toast.text
def get_avatar_title(self):
"""获取登录后的头像名称"""
title = self.driver.find_element('id', 'com.lemon.lemonban:id/fragment_my_lemon_avatar_title')
return title.text
def width(self):
"""获取app的宽度"""
return self.driver.get_window_size()['width']
def height(self):
"""获取app的高度"""
return self.driver.get_window_size()['height']
def swipe_left(self):
"""从右往左滑动"""
self.driver.swipe(start_x=self.width() * 0.9, end_x=self.width() * 0.1,
start_y=self.height() * 0.5, end_y=self.height() * 0.5)
def swipe_right(self):
"""从左往右滑动"""
self.driver.swipe(start_x=self.width() * 0.1, end_x=self.width() * 0.9,
start_y=self.height() * 0.5, end_y=self.height() * 0.5)
def swipe_up(self):
"""从下往上滑动"""
self.driver.swipe(start_x=self.width() * 0.5, end_x=self.width() * 0.5,
start_y=self.height() * 0.9, end_y=self.height() * 0.1)
def swipe_down(self):
"""从上往下滑动"""
self.driver.swipe(start_x=self.width() * 0.5, end_x=self.width() * 0.5,
start_y=self.height() * 0.1, end_y=self.height() * 0.9)
def enter(self):
"""回车"""
self.driver.press_keycode(MobileKey.ENTER)
def volume_up(self):
"""音量加"""
self.driver.press_keycode(MobileKey.VOLUME_UP)
def volume_down(self):
"""音量减"""
self.driver.press_keycode(MobileKey.VOLUME_DOWN)
def back(self):
"""返回"""
self.driver.press_keycode(MobileKey.BACK)
conftest.py代码如下:
import pytest
from appium.webdriver import Remote
from pages.home_page import HomePage
from pages.login_page import LoginPage
from pages.user_page import UserPage
def get_driver():
""""启动app"""
caps = {"platformName": "Android",
"udid": "emulator-5554",
"appPackage": "com.lemon.lemonban",
"appActivity": ".activity.WelcomeActivity",
"chromedriverExecutableDir": "E:\driver"
}
driver = Remote(command_executor='http://127.0.0.1:4723/wd/hub',
desired_capabilities=caps)
# 设置隐性等待
driver.implicitly_wait(10)
return driver
@pytest.fixture(name='app') # 设置别名代替init_app
def init_app():
"""启动app"""
d = get_driver()
yield d
d.quit()
@pytest.fixture()
def homepage(app):
"""初始化homepage"""
return HomePage(app)
@pytest.fixture()
def userpage(app):
"""初始化userpage"""
return UserPage(app)
@pytest.fixture()
def loginpage(app):
"""初始化loginpage"""
return LoginPage(app)
测试用例代码如下:
from pages.home_page import HomePage
from pages.user_page import UserPage
from pages.login_page import LoginPage
class TestLogin:
def test_login_failed(self, homepage: HomePage,
userpage: UserPage,
loginpage: LoginPage):
"""登录失败"""
homepage.enter_userpage()
userpage.click_login()
loginpage.login('1817317991', '179913')
assert loginpage.get_error_info() == '手机号码格式不正确'
def test_login_success(self, homepage: HomePage,
userpage: UserPage,
loginpage: LoginPage):
"""登录成功"""
homepage.enter_userpage()
userpage.click_login()
loginpage.login('18173179913', '179913')
assert userpage.get_uesrname() == '雨泽'
运行结果:
注意:
在app中的PO模式还是有所不同,因为好多app底部都是菜单栏,现在很多代码都被组件化了,所以我们可以把底部的菜单栏进行一下封装,这样 home_page,user_page中的类直接继承即可
新建menu.py代码如下:
from pages.native_page import NativePage
class Menu(NativePage):
"""底部菜单栏"""
locator_homepage = ('id', 'com.lemon.lemonban:id/navigation_home')
locator_tiku = ('id', 'com.lemon.lemonban:id/navigation_tiku')
locator_userpage = ('id', 'com.lemon.lemonban:id/navigation_my')
def enter_homepage(self):
self.click(self.locator_homepage)
def enter_tiku(self):
self.click(self.locator_tiku)
def enter_userpage(self):
self.click(self.locator_userpage)
user_page.py中的其他代码无需修改,只要把继承的类修改成Menu即可
home_page.py中代码可以全部删除掉,继承Menu类
login_page.py代码不用修改,因为登录页面没有底部菜单栏
当然,看自己的选择,也可以不进行组件化封装,但这是一种思路,如果太多类存在的话,维护起来也是比较麻烦的