# PO六层模型:用例test开头+精简代码
import unittest
import time
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import (
TimeoutException, NoSuchElementException, ElementClickInterceptedException
)
# ------------------------------ 1. 配置层(Config)------------------------------
class Config:
LOGIN_URL = "http://192.168.178.134:8000/admin/index.html#/login"
EDGE_DRIVER_PATH = r'F:\edge\msedgedriver.exe'
ADMIN_USERNAME = "admin"
ADMIN_PASSWORD = "123456"
WAIT_TIME = 3 # 统一等待时间
SHORT_WAIT = 1 # 短等待(仅用于元素交互间隙)
# ------------------------------ 2. 基础层(BasePage)------------------------------
class BasePage:
def __init__(self, driver):
self.driver = driver
self.driver.implicitly_wait(Config.WAIT_TIME)
self.wait = WebDriverWait(self.driver, Config.WAIT_TIME)
def open_url(self, url):
self.driver.get(url)
self.wait_for_page_load()
def wait_for_page_load(self):
try:
self.wait.until(lambda d: d.execute_script("return document.readyState") == "complete")
except TimeoutException:
raise TimeoutException("页面加载超时")
def click_element(self, locator):
try:
elem = self.wait.until(EC.element_to_be_clickable(locator))
elem.click()
except ElementClickInterceptedException:
self.driver.implicitly_wait(Config.SHORT_WAIT)
elem = self.wait.until(EC.element_to_be_clickable(locator))
elem.click()
def input_text_to_element(self, locator, text):
elem = self.wait.until(EC.visibility_of_element_located(locator))
elem.clear()
elem.send_keys(text)
if elem.get_attribute("value") != text:
raise Exception(f"输入失败:预期「{text}」,实际「{elem.get_attribute('value')}」")
def select_dropdown_option(self, select_locator, option_locator):
self.click_element(select_locator)
self.driver.implicitly_wait(Config.SHORT_WAIT)
self.click_element(option_locator)
def get_element_text(self, locator):
return self.wait.until(EC.visibility_of_element_located(locator)).text.strip()
def is_element_displayed(self, locator):
try:
self.wait.until(EC.visibility_of_element_located(locator))
return True
except TimeoutException:
return False
def handle_alert_popup(self):
try:
self.wait.until(EC.alert_is_present()).accept()
except TimeoutException:
pass
# ------------------------------ 3. 页面对象层(PageLocators)------------------------------
class PageLocators:
"""仅保留元素定位,无任何冗余内容"""
# 登录页
LOGIN_INPUT_USER = (By.XPATH, '//*[@id="app"]/div/form/div[2]/div/div[1]/input')
LOGIN_INPUT_PWD = (By.XPATH, '//*[@id="app"]/div/form/div[3]/div/div/input')
LOGIN_BTN_SUBMIT = (By.XPATH, '//*[@id="app"]/div/form/button')
# 导航菜单
MENU_USER_MANAGE = (By.XPATH, '//*[@id="app"]/div/div[1]/div[2]/div[1]/div/ul/div[2]/li/div/span')
MENU_STUDENT_LIST = (By.XPATH, '//*[@id="app"]/div/div[1]/div[2]/div[1]/div/ul/div[2]/li/ul/div[1]/a/li/span')
# 学生列表页-操作按钮
STUDENT_BTN_ADD = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/form/div[2]/div/a/button/span')
STUDENT_BTN_SUBMIT = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/form/div[10]/div/button[1]')
STUDENT_BTN_QUERY = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/form//div/button[1]/span')
STUDENT_BTN_EDIT = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[1]/div[3]/table/tbody/tr[1]/td[9]/div/a[1]/button/span')
STUDENT_BTN_DELETE = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[1]/div[3]/table/tbody/tr[1]/td[9]/div/button[2]/span')
STUDENT_BTN_STATUS_SWITCH = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[1]/div[3]/table/tbody/tr[1]/td[9]/div/button[1]/span')
# 学生列表页-输入框
STUDENT_INPUT_USERNAME = (By.XPATH, '/html/body/div/div/div[2]/section/div/form/div[1]/div/div[1]/input')
STUDENT_INPUT_PASSWORD = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/form/div[2]/div/div/input')
STUDENT_INPUT_REAL_NAME = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/form/div[3]/div/div/input')
STUDENT_INPUT_QUERY = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/form/div[1]/div/div/input')
# 学生列表页-下拉选项
STUDENT_SELECT_GRADE = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/form/div[8]/div/div/div/input')
STUDENT_OPTION_GRADE_1 = (By.XPATH, '/html/body/div[2]/div[1]/div[1]/ul/li[1]')
STUDENT_OPTION_GRADE_2 = (By.XPATH, '/html/body/div[2]/div[1]/div[1]/ul/li[2]')
# 学生列表页-验证元素
STUDENT_TEXT_FIRST_USER = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[1]/div[3]/table/tbody/tr[1]/td[2]/div')
STUDENT_TEXT_FIRST_GRADE = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[1]/div[3]/table/tbody/tr[1]/td[4]/div')
STUDENT_TEXT_STATUS = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[1]/div[3]/table/tbody/tr[1]/td[8]/div/span')
STUDENT_TEXT_NO_DATA = (By.XPATH, '//*[@id="app"]/div/div[2]/section/div/div[1]/div[3]/div/span')
# ------------------------------ 4. 页面操作层(PageActions)------------------------------
class LoginPageActions(BasePage):
"""登录页单一操作,无冗余逻辑"""
def __init__(self, driver):
super().__init__(driver)
self.loc = PageLocators()
def open_login_page(self):
self.open_url(Config.LOGIN_URL)
def input_login_username(self, username):
self.input_text_to_element(self.loc.LOGIN_INPUT_USER, username)
def input_login_password(self, password):
self.input_text_to_element(self.loc.LOGIN_INPUT_PWD, password)
def click_login_submit_btn(self):
self.click_element(self.loc.LOGIN_BTN_SUBMIT)
def is_login_success(self):
return self.is_element_displayed(self.loc.MENU_USER_MANAGE)
class StudentPageActions(BasePage):
"""学生列表页单一操作,无冗余逻辑"""
def __init__(self, driver):
super().__init__(driver)
self.loc = PageLocators()
# 导航操作
def click_user_manage_menu(self):
self.click_element(self.loc.MENU_USER_MANAGE)
def click_student_list_menu(self):
self.click_element(self.loc.MENU_STUDENT_LIST)
# 表单操作
def click_add_student_btn(self):
self.click_element(self.loc.STUDENT_BTN_ADD)
def input_student_username(self, username):
self.driver.implicitly_wait(Config.SHORT_WAIT)
self.input_text_to_element(self.loc.STUDENT_INPUT_USERNAME, username)
def input_student_password(self, password):
self.input_text_to_element(self.loc.STUDENT_INPUT_PASSWORD, password)
def input_student_real_name(self, real_name):
self.input_text_to_element(self.loc.STUDENT_INPUT_REAL_NAME, real_name)
def click_student_submit_btn(self):
self.click_element(self.loc.STUDENT_BTN_SUBMIT)
# 下拉选择
def click_grade_select_box(self):
self.click_element(self.loc.STUDENT_SELECT_GRADE)
def click_grade_1_option(self):
self.click_element(self.loc.STUDENT_OPTION_GRADE_1)
def click_grade_2_option(self):
self.click_element(self.loc.STUDENT_OPTION_GRADE_2)
# 查询操作
def input_query_keyword(self, keyword):
self.input_text_to_element(self.loc.STUDENT_INPUT_QUERY, keyword)
def click_query_btn(self):
self.click_element(self.loc.STUDENT_BTN_QUERY)
# 编辑/删除/状态切换
def click_edit_student_btn(self):
self.click_element(self.loc.STUDENT_BTN_EDIT)
def click_delete_student_btn(self):
self.click_element(self.loc.STUDENT_BTN_DELETE)
def click_status_switch_btn(self):
self.click_element(self.loc.STUDENT_BTN_STATUS_SWITCH)
# 结果验证
def get_first_student_username(self):
return self.get_element_text(self.loc.STUDENT_TEXT_FIRST_USER)
def get_first_student_grade(self):
return self.get_element_text(self.loc.STUDENT_TEXT_FIRST_GRADE)
def get_student_status_text(self):
return self.get_element_text(self.loc.STUDENT_TEXT_STATUS)
def is_no_data_displayed(self):
return self.is_element_displayed(self.loc.STUDENT_TEXT_NO_DATA)
# ------------------------------ 5. 测试数据层(TestData)------------------------------
class TestData:
"""仅保留测试数据,无冗余说明"""
STUDENT_INFO = {
"username": "赵六",
"password": "123456",
"real_name": "赵遛",
"target_grade_2": "二年级",
"status_enabled": "启用",
"status_disabled": "禁用"
}
LOGIN_INFO = {
"username": Config.ADMIN_USERNAME,
"password": Config.ADMIN_PASSWORD
}
# ------------------------------ 6. 用例层(TestCases)------------------------------
class TestStudentManagement(unittest.TestCase):
"""所有用例以test_开头,按编号命名,步骤精简"""
@classmethod
def setUpClass(cls):
# 初始化浏览器与操作对象
service = Service(Config.EDGE_DRIVER_PATH)
cls.driver = webdriver.Edge(service=service)
cls.driver.maximize_window()
cls.login_actions = LoginPageActions(cls.driver)
cls.student_actions = StudentPageActions(cls.driver)
# 登录系统
cls.login_actions.open_login_page()
cls.login_actions.input_login_username(TestData.LOGIN_INFO["username"])
cls.login_actions.input_login_password(TestData.LOGIN_INFO["password"])
cls.login_actions.click_login_submit_btn()
if not cls.login_actions.is_login_success():
raise Exception("登录失败,终止测试")
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def setUp(self):
# 用例前置:进入学生列表
self.student_actions.click_user_manage_menu()
self.driver.implicitly_wait(Config.SHORT_WAIT)
self.student_actions.click_student_list_menu()
self.student_actions.wait_for_page_load()
# 用例1:添加学生
def test_001_add_student(self):
data = TestData.STUDENT_INFO
self.student_actions.click_add_student_btn()
self.driver.implicitly_wait(Config.SHORT_WAIT)
time.sleep(1)
self.student_actions.input_student_username(data["username"])
self.student_actions.input_student_password(data["password"])
self.student_actions.input_student_real_name(data["real_name"])
self.student_actions.click_grade_select_box()
self.student_actions.click_grade_1_option()
self.student_actions.click_student_submit_btn()
self.student_actions.handle_alert_popup()
self.assertEqual(self.student_actions.get_first_student_username(), data["username"], "test_001失败")
# 用例2:查询学生
def test_002_query_student(self):
data = TestData.STUDENT_INFO
self.student_actions.input_query_keyword(data["username"])
self.student_actions.click_query_btn()
self.assertEqual(self.student_actions.get_first_student_username(), data["username"], "test_002失败")
# 用例3:修改学生年级
def test_003_edit_student_grade(self):
data = TestData.STUDENT_INFO
self.student_actions.input_query_keyword(data["username"])
self.student_actions.click_query_btn()
self.student_actions.click_edit_student_btn()
self.driver.implicitly_wait(Config.SHORT_WAIT)
self.student_actions.click_grade_select_box()
self.student_actions.click_grade_2_option()
self.student_actions.click_student_submit_btn()
self.student_actions.handle_alert_popup()
self.assertEqual(self.student_actions.get_first_student_grade(), data["target_grade_2"], "test_003失败")
# 用例4:禁用学生账号
def test_004_disable_student(self):
data = TestData.STUDENT_INFO
self.student_actions.click_status_switch_btn()
self.student_actions.handle_alert_popup()
self.assertEqual(self.student_actions.get_student_status_text(), data["status_disabled"], "test_004失败")
# 用例5:启用学生账号
def test_005_enable_student(self):
data = TestData.STUDENT_INFO
self.student_actions.click_status_switch_btn()
self.student_actions.handle_alert_popup()
self.assertEqual(self.student_actions.get_student_status_text(), data["status_enabled"], "test_005失败")
# 用例6:删除学生
def test_006_delete_student(self):
data = TestData.STUDENT_INFO
self.student_actions.input_query_keyword(data["username"])
self.student_actions.click_query_btn()
self.student_actions.click_delete_student_btn()
self.student_actions.handle_alert_popup()
self.student_actions.input_query_keyword(data["username"])
self.student_actions.click_query_btn()
self.assertTrue(self.student_actions.is_no_data_displayed(), "test_006失败")
if __name__ == "__main__":
# 按用例编号顺序执行
suite = unittest.TestSuite()
suite.addTest(TestStudentManagement("test_001_add_student"))
suite.addTest(TestStudentManagement("test_002_query_student"))
suite.addTest(TestStudentManagement("test_003_edit_student_grade"))
suite.addTest(TestStudentManagement("test_004_disable_student"))
suite.addTest(TestStudentManagement("test_005_enable_student"))
suite.addTest(TestStudentManagement("test_006_delete_student"))
unittest.TextTestRunner(verbosity=2).run(suite)还是没有在左侧单独显示测试结果
最新发布