目录
1.复杂用例的前置条件 、测试步骤
2.basepage
正文 83 28min
1.复杂用例的前置条件 、测试步骤---------以 投资用例 为例子来详细说明
根据测试步骤来进行编写
①编写投资的测试用例 test_invest.py ------之前的test_login.py测试用例编写的是TestLogin类,在投资测试用例中换个方式,直接写成函数
"""投资用例""" def test_invest_fail_not_10_times(): """投资失败:输入金额不是10的整数倍 的测试用例 测试步骤: 1.前置条件:先登录 conftest ---账户有余额 ---有标 2.首页:点击 【抢投标】 3.投标详情页:输入投标金额 4.详情页:获取结果 """
根据test_invest_fail_not_10_times()函数的docstring,投标之前的前置条件,除了登录,还需要:账户必须有余额(充值),系统有标可以投(建标、审核),那么这些条件是否都需要通过web自动化去实现???
---------不需要!!!
可以实现前置条件的方式比如:接口实现、修改数据库、手工添加、web自动化等
web自动化实现:可以每次执行之前,都自动化充值、或者加标或者,一次性充值足够的钱。但是,用web自动化实现这些前置条件,比较麻烦又比较困难,
所以,可以通过手工的方式实现前置条件。
总之一句话,这些前置条件能够满足,就OK了。------------(只要能够实现这个前置条件,就可以)
①-1前置条件--登录
在conftest中,封装登录的前置条件--------在login测试用例中,实现了test_login_success()的方法,在前置封装的登录方法中直接调用即可
"""固定文件名conftest.py,存储所有的测试夹具fixture""" import pytest from middware import handler from middware.pages.login import LoginPage from data.login_data import data_success @pytest.fixture() def driver(): from selenium import webdriver ##前置条件 #打开浏览器 driver = webdriver.Chrome() # 设置隐性等待 等待的时间就可以放在config中,直接参数调用 wait_time = handler.HandlerMiddle.yaml_data["selenium"]["wait_time"] driver.implicitly_wait(wait_time) yield driver #后置清理 driver.quit() @pytest.fixture() def login(driver): """登录""" #前置条件:登录成功 user_info = data_success[0] #登录成功数据的第一条,实现登录即可 #登录成功后进入首页 index_page = LoginPage(driver).get().login_success(username=user_info["username"], password=user_info["password"]) #返回首页 yield index_page #无后置清理工作
继续按测试步骤执行第二步:抢投标
但是并没有封装抢投标的方法,则返回到index页面进行抢投标方法的封装
②首页 点击:抢投标
定位【抢投标】按钮,来确定元素定位方式:
index.py文件中封装抢投标的方法:
"""登录成功页面""" from selenium.webdriver.common.by import By from middware.handler import HandlerMiddle class IndexPage: """登录成功""" #首页的URL URL =HandlerMiddle.yaml_data["host"] + "/Index.html" #用户账号locator user_accout_locator = (By.XPATH,'//a[@href="/Member/index.html"]') #抢投标按钮locator invest_btn_locator = (By.CLASS_NAME,'btn-special') #初始化driver def __init__(self,driver): self.driver = driver #打开首页 def get(self): self.driver.get(self.URL) return self #获取登录成功的用户名 def get_account_name(self): web_element=self.driver.find_element(*self.user_accout_locator) return web_element.text #点击抢投标 def click_invest_btn(self): self.driver.find_element(*self.user_accout_locator).click() # 返回投标详情页 return InvestPage(self.driver)
点击抢投标进入的是投标详情页----》所以抢投标的方法,返回的是投标详情页
投标详情页 没有封装,下面进行投标详情页的封装:----invest.py
③页面详情页-输入用户金额、点击投标
invest.py中 封装的类InvestPage中:
不需要URL -----因为每个详情页是不一样的,详情页是动态变化的(URL是变化的)
不需要get()方法----因为无法直接访问该详情页
下面进行投标金额输入框的元素定位:输入用户金额 ----->定位【投标】按钮并点击
定位投标按钮(不是10的整数倍)进行断言
invest.py封装好如下:
"""登录成功页面""" from selenium.webdriver.common.by import By from middware.handler import HandlerMiddle class InvestPage: """投资详情页""" #输入用户金额 user_invest_input_locator = (By.CLASS_NAME,'form-control') #投标 按钮 invest_btn_locator = (By.CLASS_NAME,'btn-special') #初始化driver def __init__(self,driver): self.driver = driver def enter_user_money(self,money): self.driver.find_element(*self.user_invest_input_locator).send_keys(money) return self def get_error_info(self): """错误信息:输入金额不是10的整数倍""" ele = self.driver.find_element(*self.invest_btn_locator) return ele.text
再次回到test_invest.py完善测试用例:
"""投资用例""" from middware.pages.index import IndexPage from middware.handler import HandlerMiddle def test_invest_fail_not_10_times(login): """投资失败:输入金额不是10的整数倍 的测试用例 测试步骤: 1.前置条件:先登录 conftest ---账户有余额 ---有标 2.首页:点击 【抢投标】 3.投标详情页:输入投标金额 4.详情页:获取结果 """ # driver = login # actual_result = IndexPage(driver).click_invest_btn().enter_user_money(8).get_error_info() actual_result = IndexPage(login).click_invest_btn().enter_user_money(8).get_error_info() #断言 try: assert actual_result == "请输入10的整数倍" except AssertionError as e: HandlerMiddle.logger.error("测试用例不通过!") raise e
84 节
运行上面的代码,出现【抢投标】按钮元素定位成功,但是点击【抢投标】按钮,并没有生效。
原因:是前后2个页面,定位class属性的value值是一样的,value=‘btn-special’
总结:
如何避免出现上下2个页面,同样的元素定位方式重合的问题?
①等待 即等待下一个页面加载完成(在下一个页面进行操作时,在初始化中加入加入等待)
----time.sleep()
----get(),调用get(),但是不是每个页面都有get()方法,比如投资详情页是变化的,每页get()方法,就无法用
----显性等待
已经加载这个页面,在初始化中完成这个页面
②检查相邻页面的元素定位方式是不是一样的。 ------需要重复查询元素,比较麻烦,不建议使用
在login 、index、invest的页面初始化中,添加显性等待:
比如:indexpage中:
title_contains:表示页面标题包含的内容
"""登录成功页面""" from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from middware.handler import HandlerMiddle from middware.pages.invest import InvestPage class IndexPage: """登录成功""" title = "项目详情" #首页的URL URL =HandlerMiddle.yaml_data["host"] + "/Index.html" #用户账号locator user_accout_locator = (By.XPATH,'//a[@href="/Member/index.html"]') #抢投标按钮locator invest_btn_locator = (By.CLASS_NAME,'btn-special') #初始化driver def __init__(self,driver): self.driver = driver try: WebDriverWait(self.driver,timeout=20).until( expected_conditions.title_contains(self.title) ) except: HandlerMiddle.logger.error("您的操作可能没有进入对应的页面:{},可能会有异常!".format(self.title))
2.BasePage --- 每个页面的通用方法放在一个公共页面类当中
1)页面行为的分类:
---某个页面的特定的行为 定位登录元素
----每个页面的通用行为 定位某个元素可见
比如等待某个元素可见,就属于通用行为:
def wait_element_visible(self,locator,timeout = 20,poll = 0.5): """显式等待元素可见 :return elem """ ele = WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until( expected_conditions.visibility_of_element_located(locator) ) return ele
单独放在某个页面里面,就不合理。应该放在通用类basepage中更为合理
2)搭建basepage框架
在pages中新建basepage.py文件,将属于的通用方法移至basepage.py中。
如下:如果别的页面要调用wait_element_visible方法,只要调用该页面的方法即可。
"""通用的方法""" from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait class BasePage: def __init__(self,driver): self.driver = driver def wait_element_visible(self,locator,timeout = 20,poll = 0.5): """显式等待元素可见 :return elem """ ele = WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until( expected_conditions.visibility_of_element_located(locator) ) return ele
那么,别的页面调用的时候,之前写的是页面自己的方法,用的是self.wait_element_visible(),-----如何继续实现这样的调用?======继承
在需要调用wait_element_visible()方法的页面中,直接继承basepage类即可。
其次,将login、index、invest页面中初始化----->等待页面加载,也可以移至basepage,
但是要注意,basepage里面只是一个公共类,不能进行初始化页面,那么这个title怎么获取?
将title设置为basepage类中的类属性即可。
公共的方法basepage就是所谓的selenium_handler.py中的SeleniumHandler,既然是公共方法,不以项目为转移的,那么就可以放在common中了。
这里的BasePage类就是SeleniumHandler啦
common中的selenium_handler.py:如下
"""通用的方法""" from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from middware.handler import HandlerMiddle class BasePage: title = None def __init__(self,driver): self.driver = driver try: WebDriverWait(self.driver,timeout=20).until( expected_conditions.title_contains(self.title) ) except: HandlerMiddle.logger.error("您的操作可能没有进入对应的页面:{},可能会有异常!".format(self.title)) def wait_element_visible(self,locator,timeout = 20,poll = 0.5): """显式等待元素可见 :return elem """ ele = WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until( expected_conditions.visibility_of_element_located(locator) ) return ele
85节
总结basePage:----思想:类的继承
①不以项目为转移,每个页面都能直接调用,的页面的封装;放在一个类中,这个类就是basepage
②basepage是每个页面的父类(可以直接通过self直接调用basepage类中的方法)
下面在basepage中继续封装一些通用的方法:等待元素可以被点击、鼠标操作、窗口滚动等
"""通用的方法""" from selenium.webdriver import ActionChains from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from middware.handler import HandlerMiddle class BasePage: title = None def __init__(self,driver): self.driver = driver try: WebDriverWait(self.driver,timeout=20).until( expected_conditions.title_contains(self.title) ) except: HandlerMiddle.logger.error("您的操作可能没有进入对应的页面:{},可能会有异常!".format(self.title)) def wait_element_visible(self,locator,timeout = 20,poll = 0.5): """显式等待元素可见 :return elem """ ele = WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until( expected_conditions.visibility_of_element_located(locator) ) return ele def wait_element_clickable(self,locator,timeout = 20,poll = 0.5): """等待某个元素被点击""" ele = WebDriverWait(self.driver,timeout=timeout).until(expected_conditions.element_to_be_clickable(locator)) return self def wait_element_presence(self,locator,timeout = 20,poll = 0.5): """等待某个元素可见""" ele = WebDriverWait(self.driver,timeout=timeout).until(expected_conditions.presence_of_all_elements_located(locator)) return self def click(self,locator): """点击某个元素""" ele=self.wait_element_visible(locator).click() return self def enter_info(self,locator,value=None): """输入信息""" ele = self.wait_element_presence(locator).send_keys(value) return self def window_scroll(self,width=None,height=None): """窗口滚动""" if width==None: width="0" if height == None: height="0" js_code = "window.scrollTo({},{})".format(width,height) self.driver.execute_script(js_code) return self def mouse_moveto(self,locator): """鼠标操作 移动到某个元素""" ele= self.driver.find_element(*locator) ActionChains(self.driver).move_to_element(ele).perform() return self def switch_iframe(self,locator,timeout=20): """切换到iframe""" WebDriverWait(self.driver,timeout=timeout).until(expected_conditions.frame_to_be_available_and_switch_to_it(locator)) return self
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律