依然使用了常见的po 模式
- 目录结构
- config
- config.ini 修改目标路由与相关配置
- elements
- 抽象页面空间元素
- method
- 抽象页面方法
- TestCase
- 测试用例
- utils
- Assert 自定义断言
- chromedriver 浏览器驱动 需匹配版本 非linux 环境无视
- Driver 初始化driver
- graphical 图片相对定位
- log 日志
- seleniumBase api封装
- seleniumOpt opt定义
- config
根据判断运行环境 选择不同的chrome driver 或者使用 ChromeDriverManager 自动下载
class Driver:
@classmethod
def driver(cls):
try:
# select local or docker
# if platform == "Darwin":
# url = Config().get_conf("dockerHub", "url")
# driver = webdriver.Remote(url, desired_capabilities=capabilities(),
# options=options())
# # else:
if platform.system() == "Linux":
executable_path = os.path.join(os.path.dirname(__file__), "chromedriver")
driver = webdriver.Chrome(executable_path=executable_path, options=options())
else:
executable_path = ChromeDriverManager().install()
driver = webdriver.Chrome(executable_path=executable_path, options=options())
driver.implicitly_wait(20)
log.info(f"driver:{driver.name}")
return driver
except BaseException as e:
log.error(e)
raise e
自定义driver 的options 配置
def options():
"""
浏览器设置
:return:
"""
_options = ChromeOptions()
# # 设置chrome为手机界面
# mobileEmulation = Config().get_conf("seleniumOpt", "mobileEmulation")
# if mobileEmulation == 'true':
# mobile_emulation = {"deviceName": "Galaxy S5"}
# _options.add_experimental_option("mobileEmulation", mobile_emulation)
if platform.system() == 'Darwin':
pass
else:
_options.add_argument('--headless')
_options.add_argument('--no-sandbox')
_options.add_argument('--disable-gpu')
_options.add_argument('--disable-dev-shm-usage')
return _options
Base类
封装常用API 把driver 实例化传入
查找元素尽量使用显示等待 expected_conditions
使用了Faker 可随意生成测试数据
查找元素失败 使用allure 上传截图
class SeleniumBaseConfig:
faker = Faker(locale="zh_CN")
Assert = Assert
log = get_log()
def __init__(self, driver: webdriver):
"""
:param driver: 驱动
"""
self.driver = driver
def find_element(self, locator: tuple, timeout=5):
"""
find_element
:param locator: (xpath,xxx)(id,xxx)
:param timeout: timeout
:return:
"""
try:
element = WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator))
self.log.info(f"[{self.find_element.__name__}] locator: {locator} element: {str(element)}")
return element
except WebDriverException as e:
self.log.error(e)
allure.attach(self.get_png(), "异常截图", allure.attachment_type.PNG)
allure.attach("未找到: {}, 详细信息如下: {}".format(locator, e))
raise e
自定Assert
很常规 没什么可说的
class Assert:
@staticmethod
def assert_unequal(exp, res):
try:
log.info("ASSERT UNEQUAL: EXPECT IS [{}] RESULT IS [{}]".format(exp, res))
assert exp != res
return True
except AssertionError as e:
log.error("ASSERT UNEQUAL ERROR: EXPECT IS [{}] RESULT IS [{}]".format(exp, res))
raise e
关于页面元素
根据op 依旧对页面抽象成类 并继承SeleniumBaseConfig
元素类1一对1配套
class RegisterMethod(SeleniumBaseConfig):
def __init__(self, driver):
super().__init__(driver)
def register(self, mobile: str):
"""
注册
:param mobile
:return:
"""
self.send_keys(RegisterLogin.INPUT_NUMBER, mobile)
self.click(RegisterLogin.NEXT_STEP_BUTTON)
time.sleep(1)
对应元素类
class Register:
# ================== 公共 ================
# 下一步
NEXT_STEP_BUTTON = ("xpath", "//button")
# 返回
RETURN = ('xpath', '//p[@]')
# input 错误信息
INPUT_ERROR_INFO = ("xpath", '//p[@]')
# ================== home界面 ================
CREATE_BUTTON = ("xpath", "//div[text()='创建']") # 创建button
JOIN_BUTTON = ("xpath", "//div[text()='加入']") # 创建button
INPUT_TEAM_NAME = ("xpath", '//input[@placeholder="请输入团队名称"]') # 输入团队名称
INPUT_YOUR_NAME = ("xpath", '//input[@placeholder="请输入你的姓名"]') # 输入个人名称
测试用例
@pytest.mark.P3
@allure.title("创建新团队:姓名输入汉字、英文、数字字符")
@pytest.mark.flaky(reruns=1, reruns_delay=2)
def test_create_team_username(self):
"""
姓名输入汉字、英文、数字字符
1.register
2.create team
3.create_team_username
"""
pics = []
self.driver.register(self.mobile)
exp = "true"
res = self.driver.check_button_disabled()
self.driver.allure_report(self.test_create_team_username, pics, exp, res)
self.driver.Assert.assert_equal(exp, res)
Config 配置
一般配置host 等数据 使用ConfigParser读取写入
config.ini
[loginParam] username = username password = 123456 [domain] cm = xx
class Config:
def get_conf(self, title, value):
"""
read .ini
:param title:
:param value:
:return:
"""
log.info("{} : {}:{}".format(self.get_conf.__name__, title, value))
return self.config.get(title, value)
def set_conf(self, title, value, text):
"""
change .ini
:param title:
:param value:
:param text:
:return:
"""
log.info("{} : {}:{}:{}".format(self.set_conf.__name__, title, value, text))
self.config.set(title, value, text)
with open(self.config_path, 'w+') as f:
return self.config.write(f)
main 启动
使用sys.argv 获取命令行自定义参数
if len(sys.argv) > 3:
runCase = sys.argv[1] # P1 P2 P3 ALL 指定运行的用例等级
if runCase != "ALL":
case_path = f"-m={runCase}"
xml = sys.argv[2] # 报告路径
if xml:
xml_report_path = xml
BUILD_NUMBER = sys.argv[3]
args = ["-s", "-v", "-n=auto", case_path, '--alluredir', xml_report_path, '--clean-alluredir']
pytest.main(args=args)
pytest
pytest 有很多好用的功能、比如失败重跑、多进程并行用例、fixture 测试用例参数化等等
详见 gitHUB-SeleniumUIAuto 中的 readme



