1. 目的
界面交互作为黑盒测试内容中重要的一环,在广大的测试人员群体中几乎成了入行的第一个接触内容,执行测试任务时站在客户角的度出发是每个测试人员都必须做到的基本条件,而模拟客户的日常业务操作,界面交互操作就成为了重中之重。同样的在自动化测试中,我们该如何更好的将界面交互操作融入到实际的自动化测试代码和框架中呢,今天博主就带着大家来看一看界面交互的一些进阶设计方法。
2. 作用
那么在自动化测试中界面交互具体的实际意义有哪些呢?首先从业务层面来看,测试人员为了更接近真实用户的日常业务行为,必定需要设计一系列的对应界面交互操作,因为复杂交互测试会涉及多个元素之间的复杂交互和页面状态的变化,更接近真实用户在web被测对象中的实际操作,比起无计划的胡乱操作,通过特定的界面交互操作可以有效的模拟用户实际的使用场景,从而更全面地验证应用程序的功能和用户体验。那么与之相呼应的就是一旦形成有明确目的性的界面交互操作规划之后,该功能模块的测试覆盖率自然而然的就会被提升上来,我们所熟知的界面交互,除了一些组件的操作之外,动态内容、异步请求、页面跳转、弹出框交互等等也是经常会涉及到。有了这些元素的交叉与组合,测试人员就可以发现更多的潜在问题,从而提高测试的覆盖率。
另外,除了界面的基础交互之外,数据的交换(前端与后端的逻辑处理)同样也可以借由界面交互的检查来进行验证。当然,这需要之前的有效交互操作规划来作为大前提。比如通过数据表单提交和后台数据的处理,可以验证前后端数据的传递是否正确,这样的场景相信大家应该都不会陌生,那么如何有效的排列组合各类的表单提交与数据检查就成为了是否成功执行该类测试场景的核心因素之一。
3. 实操
好了,以上是对于界面交互的一些大致内容进行了一些了解,那我们接下来以一个实际的业务案例来进行界面交互在自动化测试中的应用方法。
3.1 场景介绍与拆分
测试业务场景:某金融系统中,用户希望申请一笔贷款。贷款申请涉及填写个人信息、选择贷款产品、填写贷款金额、选择还款期限、上传必要资料等步骤,同时系统会根据用户信息和贷款产品进行额度计算和还款计划展示。在申请过程中,可能会遇到不同的验证和提示信息,用户需要正确填写和处理各项信息,以成功提交贷款申请。
那接下来我们将业务场景进行最基本的业务流步骤拆解,这与我们设计测试代码的覆盖范围与执行步骤、界面交互有很重要的联系,所以拆解需要尽量的清晰与简洁。
对应的预期结果
3.2 GWT表设计
基于以上的业务场景与步骤拆解,我们已经可以明确的看到其中的对应测试范围与功能点了,那么在此基础上我们就可以将以上这些进行系统的输出,形成对应的测试故事了。
3.3 代码设计
当我们整理出以上的这些信息之后,就可以正式的开始设计对应的测试代码了,当然这里只是简单的演示传统业务下我们该如何将界面交互测试的内容融入自动化测试框架中,其中的很多步骤(测试用例的设计,代码的单元测试,优化迭代)这里碍于篇幅就不展开细说了,有疑问的可以留言或私信博主。
与大部分的UI自动化测试框架一样,我们可以将需要测试的业务部分拆分成三部分,base、page_objects、test_loan_application分别对应三个python脚本,base就不用多说了,一些基础驱动方法都是放在这里面的,page_objects看到名字就知道我们基于PO模式将页面的元素封装在这里面,而test_loan_application当然就是我们的测试用例啦。
话不多说,我们先来设计base脚本:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
def __init__(self, driver):
self.driver = driver
def wait_for_element_to_be_visible(self, by, locator, timeout=10):
wait = WebDriverWait(self.driver, timeout)
return wait.until(EC.visibility_of_element_located((by, locator)))
class BaseTest:
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.implicitly_wait(5)
cls.driver.maximize_window()
@classmethod
def tearDownClass(cls):
cls.driver.quit()
base脚本没什么好多说的,属于是常规操作了,需要说明的就是base中加入了对应异步通信的显式等待,这个大家见仁见智,自由发挥即可。接下来是page_objects,页面上的可见对象(测试对象)包括一些常见组件(按钮、下拉框等)全部封装在其中,方便后续的测试用例中的复用。
from selenium.webdriver.common.by import By
from base import BasePage
class LoginPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.username_input = (By.ID, "username")
self.password_input = (By.ID, "password")
self.login_button = (By.ID, "login_button")
def login(self, username, password):
self.driver.get("https://example-bank.com/login")
self.driver.find_element(*self.username_input).send_keys(username)
self.driver.find_element(*self.password_input).send_keys(password)
self.driver.find_element(*self.login_button).click()
class LoanApplicationPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.product_type_dropdown = (By.ID, "product_type")
self.loan_amount_input = (By.ID, "loan_amount")
self.repayment_period_dropdown = (By.ID, "repayment_period")
self.upload_file_input = (By.ID, "upload_file")
self.submit_button = (By.ID, "submit_button")
def apply_for_loan(self, product_type, loan_amount, repayment_period, file_path):
self.driver.get("https://example-bank.com/loan_application")
self.driver.find_element(*self.product_type_dropdown).send_keys(product_type)
self.driver.find_element(*self.loan_amount_input).send_keys(loan_amount)
self.driver.find_element(*self.repayment_period_dropdown).send_keys(repayment_period)
self.driver.find_element(*self.upload_file_input).send_keys(file_path)
self.driver.find_element(*self.submit_button).click()
def click_calculate_button(self):
calculate_button = self.wait_for_element_to_be_visible(By.ID, "calculate_button")
calculate_button.click()
def select_repayment_period(self, period):
repayment_period_dropdown = self.wait_for_element_to_be_visible(By.ID, "repayment_period")
select = Select(repayment_period_dropdown)
select.select_by_visible_text(period)
def accept_risk_warning(self):
accept_button = self.wait_for_element_to_be_visible(By.ID, "accept_button")
accept_button.click()
def risk_assessment(self):
assessment_button = self.wait_for_element_to_be_visible(By.ID, "assessment_button")
assessment_button.click()
在test_loan_application中,我们主要对测试额度计算、还款计划展示、贷款的申请结果进行断言,在断言之前我们针对业务流程进行对应的界面交互操作,当然真实的业务肯定远远不止如此,大家只需要理解如何封装各类组件并在用例中有计划的调用即可。
import unittest
from base import BaseTest
from page_objects import LoginPage, LoanApplicationPage
class TestLoanApplication(BaseTest):
def test_loan_application(self):
login_page = LoginPage(self.driver)
login_page.login("testuser", "testpass")
loan_application_page = LoanApplicationPage(self.driver)
loan_application_page.apply_for_loan("Personal Loan", "5000", "12 months", "path/to/uploaded/file.pdf")
# 添加异步通信和显式等待,等待贷款申请状态显示并验证
loan_application_page.wait_for_element_to_be_visible(By.ID, "loan_application_status", timeout=20)
status = self.driver.find_element(By.ID, "loan_application_status").text
assert "Successful" in status, "贷款申请状态错误"
# 测试额度计算和还款计划展示
loan_amount = "5000"
repayment_period = "12 months"
expected_loan_amount = "5000"
expected_repayment_plan = "Monthly Installments: 12\nAmount per Installment: 416.67"
loan_application_page.fill_loan_amount_and_repayment_period(loan_amount, repayment_period)
loan_application_page.click_calculate_button()
loan_application_page.wait_for_element_to_be_visible(By.ID, "calculated_loan_amount", timeout=10)
calculated_loan_amount = self.driver.find_element(By.ID, "calculated_loan_amount").text
assert calculated_loan_amount == expected_loan_amount, "额度计算错误"
repayment_plan = self.driver.find_element(By.ID, "repayment_plan").text
assert repayment_plan == expected_repayment_plan, "还款计划展示错误"
# 处理风险提示
risk_warning = loan_application_page.get_risk_warning_text()
if risk_warning:
loan_application_page.accept_risk_warning()
else:
# 如果没有风险提示,进行投资风险评估
loan_application_page.risk_assessment()
# 选择还款期限
new_repayment_period = "24 months"
loan_application_page.select_repayment_period(new_repayment_period)
# 提交贷款申请
loan_application_page.submit_loan_application()
loan_application_page.wait_for_element_to_be_visible(By.ID, "loan_application_status", timeout=20)
status = self.driver.find_element(By.ID, "loan_application_status").text
assert "Successful" in status, "贷款申请状态错误"
if __name__ == "__main__":
unittest.main()
4. 后话
看完以上的这个实操例子之后不知道对大家是否有所启发,其实不是说一定要用以上的方法来进行测试任务的拆解,在这里只想告诉大家,在自动化UI测试中,面对界面交互场景的测试时,我们需要有计划的对界面交互操作进行设计与实施,至于方法,这里只是给出了其中的一种思路而已,相信大家在日后的工作中一定会整理总结出适用与自己与团队的一套设计与执行的方法。