改了元素位置怎么办?重构自动化脚本就可以啦

Pytest 测试框架

 

把豆瓣登录的用例结合 Pytest 来编写如下:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
import timefrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitimport pytestdouban_account = {"username":"****", "password":"****"}def login(driver, account):    driver.get("https://www.douban.com/")    login_frame = driver.find_element_by_css_selector("div.login iframe")    WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it(login_frame))    driver.find_element_by_css_selector("li.account-tab-account").click()    driver.find_element_by_css_selector("#username").send_keys(account["username"])    driver.find_element_by_css_selector("#password").send_keys(account["password"])    driver.find_element_by_css_selector(".account-form-field-submit").click()    time.sleep(2)    driver.switch_to.default_content()class TestDouban(object):    def setup_class(self):        self.driver = webdriver.Chrome()        self.driver.maximize_window()    def teardown_class(self):        self.driver.quit()    def test_login(self):        login(self.driver, douban_account)        assert WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.CLASS_NAME, "nav-user-account")))if __name__ == "__main__":    pytest.main()

 

这时候,如果元素的位置发生了变化,需要修改定位方式,那么所有用到这个元素的测试用例都需要修改,代码维护程度依旧很高,这时候我们就需要用到 Page Object(PO)模式来重构自动化脚本。

 

 

02

图片

PO 模型简介

 

如果 100 个不同的脚本使用了相同的页面元素,只要该元素做了任何更改,那么就需要改动所有的脚本,不仅耗时,而且容易出错。

为了解决这个问题,我们可以为每个 Web 页面创建一个单独的类文件,封装该 Web 页面的元素以及操作,那么后续需要用到该 Web 页面元素或操作的脚本都可以调用这个类。如果该 Web 页面元素发生了变化,只需要修改相应的类文件即可,这种方式就是页面对象模型(POM),它有助于提高代码的可读性、可维护性和可重用性。

优点:

·减少代码的重复

·提高测试用例的可读性

·提高测试用例的可维护性

PO的实现:将单个页面上所有Web元素和在这些Web元素的操作方法都放在一个Page类文件中(所有的page文件可以统一放到 page_object 模块),而用于验证的测试用例则单独作为测试方法的一部分(可以放到 test_case 模块)。我们以登录豆瓣为例:

文件结构:

 

图片

 

示例代码:

1. 首先创建一个 demo_login_page.py,封装登录页面的元素和操作

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
import timefrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitclass LoginPage:    def __init__(self, driver):        self.driver = driver    def open_url(self, url):        self.driver.get(url)    def login(self, account, password):        login_frame = self.driver.find_element(*("css selector", "div.login iframe"))   # 切换 iframe        WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it(login_frame))        self.driver.find_element(*("css selector", "li.account-tab-account")).click()    # 点击密码登录        self.driver.find_element(*("id", "username")).send_keys(account)  # 输入账号        self.driver.find_element(*("id", "password")).send_keys(password)     # 输入密码        self.driver.find_element(*("css selector", ".btn-account")).click()      # 点击登录        time.sleep(2)        self.driver.switch_to.default_content()

 

注:find_element(*("css selector", "div.login iframe")) 等同于 find_element_by_css_selector("div.login iframe")

2. 首先创建一个 demo_home_page.py,封装首页的元素和操作(如获取登录账号信息,判断是否登录成功)

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
from selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECclass HomePage:    def __init__(self, driver):        self.driver = driver    def is_account_info_exist(self):        try:            WebDriverWait(self.driver, 30).until(EC.visibility_of_element_located(("class name", "nav-user-account")))            return True        except Exception:            return False

 

3. 在 test_demo.py 中编写测试用例,所有的元素获取和操作都调用对应的页面类来实现

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
import pytestfrom selenium import webdriverfrom po.page_object.demo_home_page import HomePagefrom po.page_object.demo_login_page import LoginPageclass TestLogin():    def setup_class(self):        self.driver = webdriver.Chrome()        self.driver.maximize_window()        self.driver.implicitly_wait(5)    def teardown_class(self):        self.driver.quit()    def test_demo(self):        self.login_page = LoginPage(self.driver)        self.home_page = HomePage(self.driver)        self.login_page.open_url("https://www.douban.com/")        self.login_page.login("****", "****")        assert self.home_page.is_account_info_exist()if __name__ == "__main__":    pytest.main()

 

这样,无论 login 、home 页面元素如何变化,都只需要修改相应页面中的代码即可,大大降低了自动化脚本的维护成本及因脚本更新引发的出错概率。

 

 

03

图片

优化

 

PO 模式让我们仅需要在Page页面类单个文件中对变化的元素定位进行更新,但是一个项目一般都有很多个页面,如果页面都发生了变化仍然需要修改多个页面类文件,那是否尽可能使任何一个自动化脚本文件都不发生变更呢?很简单,只需要把这些定位作为数据单独存放在配置文件中即可(配置文件可以是 ini、csv、excel 等),以 ini 配置文件为例

文件结构:

 

图片

 

示例代码:

1. page_object 模块用来存放页面操作

demo_login_page.py 用来存放登录页面的操作

 

import osimport timefrom configparser import ConfigParserfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitclass LoginPage:    def get_locator(self):        conf = ConfigParser()        conf.read(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\page_locator\\demo_locator.ini", encoding="utf-8")        self.url = conf.get("Base", "url")        section = "LoginPageLocator"        # eval 将字符串元组转换为元组        self.login_frame = eval(conf.get(section, "login_frame"))        self.login_by_pwd_btn = eval(conf.get(section, "login_by_pwd_btn"))        self.account = eval(conf.get(section, "account"))        self.pwd = eval(conf.get(section, "pwd"))        self.login_btn = eval(conf.get(section, "login_btn"))    def __init__(self, driver):        self.driver = driver        self.get_locator()    def open_url(self):        self.driver.get(self.url)    def input(self, locator, value):        self.driver.find_element(*locator).send_keys(value)    def click(self, locator):        self.driver.find_element(*locator).click()    def change_frame(self, locator):        frame = self.driver.find_element(*locator)        WebDriverWait(self.driver, 10).until(EC.frame_to_be_available_and_switch_to_it(frame))    def login(self, account, password):        self.change_frame(self.login_frame) # 切换iframe        self.click(self.login_by_pwd_btn)   # 点击密码登录        self.input(self.account, account)   # 输入账号        self.input(self.pwd, password)  # 输入密码        self.click(self.login_btn)  # 点击登录        time.sleep(2)        self.driver.switch_to.default_content() # 切换到原来的页面    def logout(self):        pass

 

以上脚本将页面上的不同操作都封装成了方法,使脚本看起来更为简洁,但是如果页面很多,每个页面都要封装点击、输入的操作会特别繁琐,可以把所有的公共方法抽离出来,封装到一个公共模块中,将在下一节详细介绍~~

demo_home_page.py 用来存放home页面的操作

 

import osfrom configparser import ConfigParserfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECclass HomePage:    def get_locator(self):        conf = ConfigParser()        conf.read(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\page_locator\\demo_locator.ini", encoding="utf-8")        section = "HomePageLocator"        self.account_info = eval(conf.get(section, "account_info"))    def __init__(self, driver):        self.driver = driver        self.get_locator()    def is_account_info_exist(self):        try:            WebDriverWait(self.driver, 30).until(EC.visibility_of_element_located(self.account_info))            return True        except Exception:            return False

 

注:ConfigParser 模块用于读取配置文件,用法请自行百度。

2. page_locator 模块用来存放通用地址和页面元素的定位,如 demo_locator.ini 用来存放登录页面的元素定位等信息,可以把多个页面的 locator 都存放在一个 ini 文件中,这样只需要修改 ini 文件即可

 

[Base]url = https://www.douban.com/;"id"、"xpath"、"link text"、"partial link text"、"name"、"tag name"、"class name"、"css selector"[LoginPageLocator]# iframelogin_frame = ("css selector", "div.login iframe")# 密码登录按钮login_by_pwd_btn = ("css selector", ".account-tab-account")# 账号输入框account = ("id", "username")# 密码输入框pwd = ("id", "password")# 登录豆瓣按钮login_btn = ("css selector", ".btn-account");登录错误信息error_msg = ("css selector", ".account-form-error span")[HomePageLocator]# 账号信息account_info = ("class name", "nav-user-account")

 

3. test_case 模块用来编写测试用例

 

import pytestfrom selenium import webdriverfrom po.page_object.demo_home_page import HomePagefrom po.page_object.demo_login_page import LoginPageclass TestLogin():    def setup_class(self):        self.driver = webdriver.Chrome()        self.driver.maximize_window()        self.driver.implicitly_wait(5)    def teardown_class(self):        self.driver.quit()    def test_demo(self):        self.login_page = LoginPage(self.driver)        self.home_page = HomePage(self.driver)        self.login_page.open_url()        self.login_page.login("****", "****")        assert self.home_page.is_account_info_exist()if __name__ == "__main__":    pytest.main()

 

现在所有的页面元素定位都写到一个 locator.ini 文件中,当页面元素的定位方式发生变化时,只需要修改 locator.ini 一个文件即可,大家可以自行挑选合适的配置文件保存元素定位信息。

上面是我收集的一些视频资源包

对于软件测试的的朋友来说应该是最全面最完整的备战仓库了,为了更好地整理每个模块,我也参考了很多网上的优质博文和项目,力求不漏掉每一个知识点,很多朋友靠着这些内容进行复习,拿到了BATJ等大厂的offer,这个仓库也已经帮助了很多的软件测试的学习者,希望也能帮助到你!

关注我的微信公众号【程序员二黑】免费获取

如果你对软件测试、接口测试、自动化测试、面试经验交流感兴趣欢迎加入:软件测试技术群:785128166 里面有大牛分享学习经验
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员二黑

V:testerhei

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值