软件测试专栏
实战相关知识
PO模式(Page Object模式)是一种软件测试自动化设计模式,它将网页封装成对象,通过操作对象的方法来完成自动化测试,从而将测试代码与页面逻辑分离,提高了测试代码的可维护性和可重复使用性。
在PO模式中,每个页面或页面的一部分(如表单、菜单)都被封装到一个对象里,这个对象提供了操作页面元素的方法和属性。测试脚本只需要调用这些方法和属性,而不需要了解页面的具体实现细节。当页面元素改变时,只需要更新对象,而不需要修改测试脚本。
PO模式的优点包括:
- 提高测试代码的可维护性和可重复使用性。
- 降低测试代码的耦合性,测试脚本只需要关注业务逻辑,而不用关注页面实现细节。
- 增强测试脚本的可读性和可维护性。
- 支持多浏览器测试,只需要针对不同的浏览器实现不同的页面对象。
- 提高测试自动化的效率和稳定性,避免了测试人员重复操作页面的失误。
实战内容
访问“新浪微博”网页 ,完成“搜索”、“登录”和“发微博”三个测试用例。
(1)测试用例设计
- 登录测试用例 (test_login):
实例化LoginPage对象。
调用click_login_button方法,点击登录按钮。
调用verify_login_success方法,验证登录微博是否成功。 - 发布微博测试用例 (test_post_weibo):
实例化HomePage对象。
调用input_weibo_content方法,输入微博内容。
调用click_publish_button方法,点击发布按钮。
调用verify_publish_success方法,验证微博发布是否成功。 - 搜索测试用例 (test_search):
实例化SearchPage对象。
调用input_keyword_and_search方法,输入关键字并搜索。
(2)测试框架设计
通过使用PO模式将页面元素的定位以及元素的操作分离出来,测试用例脚本直接调用这些封装好的元素操作来组织测试用例,从而实现测试用例脚本和元素定位、操作的分离。
定义了三个页面类:LoginPage、HomePage和SearchPage,分别封装了登录页面、主页和搜索页面的操作和验证方法。样我们就可以将与页面操作相关的代码封装到对应的Page Object类中,以实现更好的代码组织和可维护性。
在测试方法中,我们通过创建相应的Page Object实例来执行页面操作,例如在test_login中使用LoginPage执行登录操作,在test_post_weibo中使用HomePage执行发微博操作,在test_search中使用SearchPage执行搜索操作。这样可以提高代码的可读性和可维护性。
使用了pytest作为测试框架,通过@pytest.fixture装饰器定义了setup函数,使用@pytest.mark.usefixtures装饰器将setup函数应用于测试类。测试类中定义了测试方法,最后使用pytest.main函数运行测试用例,并生成 HTML 格式的报告文件。如果该脚本直接被执行,则会运行测试用例并关闭 mpmath 中的 fp 函数。
封装登录页面操作和验证方法
封装主页页面操作和验证方法
封装搜索页面操作和验证方法
“@pytest.fixture(scope=“class”)”是 Pytest 测试框架中的装饰器,我们使用了 scope=“class” 参数,它指定了该函数的作用范围为测试类级别。在 setup 夹具函数中,我们做了以下几个操作:创建了一个Chrome浏览器的实例,即 webdriver.Chrome()。最大化了浏览器窗口。设置了隐式等待时间为 10 秒,即在查找元素时,如果元素未立即出现,会等待最多10秒。打开了微博网站,即driver.get(“https://weibo.com”)。将创建的浏览器实例传递给测试类,通过request.cls.driver = driver。使用yield将控制权交还给测试类。在测试结束后,关闭浏览器,即driver.quit()。
“这样,测试类中的测试方法就可以共享同一个浏览器实例,并在每个测试方法运行之前和之后进行相应的设置和清理工作。
setup函数
“@pytest.mark.usefixtures(“setup”)”是 Pytest 测试框架中的装饰器,它告诉Pytest在运行测试类的每个测试方法之前先运行指定的函数(setup),这样可以确保在每个测试方法开始之前都进行一些必要的设置或准备工作。这样可以提高测试代码的可读性和可维护性,并减少重复代码。
测试类
(3)测试报告
运行结果
登录成功截图
发微博成功截图
搜索成功截图
测试报告
操作异常问题与解决方案
- 异常问题:
页面对象无法找到页面上的元素,导致定位失败。 - 解决方法:
检查页面对象中的元素定位方法和表达式是否正确。确认页面结构和元素属性是否发生了变化。可以使用开发者工具或浏览器插件来验证元素定位表达式是否有效。
- 异常问题:
在操作页面元素时,等待超时导致无法执行操作。 - 解决方法:
使用适当的等待机制等待元素的出现、可点击或可见状态。可以使用显式等待(WebDriverWait)等待元素出现或满足特定条件。调整等待时间,确保足够的时间加载页面或元素。
- 异常问题:
在测试用例中,断言条件未满足,导致断言失败。 - 解决方法:
检查断言条件是否正确,确保预期结果与实际结果一致。仔细查看测试数据、操作流程或页面变化等因素,确保断言条件正确。
- 异常问题:
在页面对象操作元素时,元素处于不可交互状态,无法执行操作。 - 解决方法:
确认元素是否被其他元素覆盖、隐藏或禁用。等待页面元素可交互的状态,如等待其他元素完成加载或动画。确保操作元素的可见性和可点击性。
附录
具体的测试脚本、配置文件等源码。
第一层:将所有元素对象定位器放到一个文件(locator.py)
from selenium.webdriver.common.by import By class LoginPageLocators: LOGIN_BUTTON = (By.XPATH, "//*[@id='__sidebar']/div/div[2]/div[1]/div/button") class HomePageLocators: CONTENT_INPUT = (By.XPATH, "//*[@id='homeWrap']/div[1]/div/div[1]/div/textarea") PUBLISH_BUTTON = (By.XPATH, "//*[@id='homeWrap']/div[1]/div/div[4]/div/div[4]/button") class SearchPageLocators: SEARCH_BOX = (By.XPATH, "//*[@id='app']/div[2]/div[1]/div/div[1]/div/div/div[1]/div/div[2]/div/span/form/div/input") POST_BUTTON = (By.XPATH, "//*[@id='app']/div[2]/div[1]/div/div[1]/div/div/div[1]/div/div[2]/div/div/div/div[1]/div[1]/div/a/div/div/div/div[2]") class SuccessMessageLocators: SUCCESS_MESSAGE = (By.XPATH, "//*[@id='scroller']/div[1]/div[1]/div/article/div[2]/div/div/div")
第二层:将所有元素操作放到一个文件(elements.py)
import time from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from 自动化实验四.PageObject.locator import LoginPageLocators, HomePageLocators, SearchPageLocators, SuccessMessageLocators class LoginPage: def __init__(self, driver): self.driver = driver def click_login_button(self): login_button = self.driver.find_element(*LoginPageLocators.LOGIN_BUTTON) login_button.click() time.sleep(2) def verify_login_success(self): assert "微博 – 随时随地发现新鲜事" in self.driver.title class HomePage: def __init__(self, driver): self.driver = driver def input_weibo_content(self, content): content_input = self.driver.find_element(*HomePageLocators.CONTENT_INPUT) content_input.clear() content_input.send_keys(content) def click_publish_button(self): publish_button = self.driver.find_element(*HomePageLocators.PUBLISH_BUTTON) publish_button.click() time.sleep(3) def verify_publish_success(self): success_message = self.driver.find_element(*SuccessMessageLocators.SUCCESS_MESSAGE) assert success_message.is_displayed() class SearchPage: def __init__(self, driver): self.driver = driver def input_keyword_and_search(self, keyword): search_box = self.driver.find_element(*SearchPageLocators.SEARCH_BOX) search_box.send_keys(keyword) time.sleep(3) post_button = self.driver.find_element(*SearchPageLocators.POST_BUTTON) post_button.click() time.sleep(3)
第三层:将公共的业务场景封装到一个文件中(test_weibo_ui.py)
import pytest from mpmath import fp from selenium import webdriver from 自动化实验四.Scenario.elements import LoginPage,HomePage,SearchPage @pytest.fixture(scope="class") def setup(request): driver = webdriver.Chrome() driver.maximize_window() driver.implicitly_wait(10) driver.get("https://weibo.com") request.cls.driver = driver yield driver.quit() @pytest.mark.usefixtures("setup") class TestWeiboUI: def test_login(self): login_page = LoginPage(self.driver) login_page.click_login_button() login_page.verify_login_success() def test_post_weibo(self): home_page = HomePage(self.driver) home_page.input_weibo_content("今天好热啊!--20215120808") home_page.click_publish_button() home_page.verify_publish_success() def test_search(self): search_page = SearchPage(self.driver) search_page.input_keyword_and_search("自动化") if __name__ == '__main__': pytest.main(['-v', '--html=test_weibo_ui.html']) fp.close()