使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 27--二次封装方法--优化断言结果

测试学习记录,仅供参考!

优化断言结果

1、优化代码脚本,找到在项目根目录文件夹下 testcase 软件包 login 目录文件下 test.login.py 文件;

可以清楚的看到是把断言写到测试用例里面中去了,所以得优化代码,把断言中的"元素定位"放到页面类中去管理;

复制代码
import pytest
from selenium.webdriver.common.by import By
from time import sleep
from pageObject.login_page.login_page import LoginPage
from util_tools.handle_data.operateJson import read_json
# from util_tools.handle_data.readYaml import read_yaml
# from util_tools.handle_data.operateExcel import ExcelDataReader

class TestLogin:

    # 使用参数化实现一条测试用例跑多种场景--可迭代对象里面有多少组数据就跑多少次测试用例
    @pytest.mark.parametrize('data', read_json('./data/login.json'))
    # 用一个 data(需与上面保持一致) 参数去接收参数化数据
    def test_login_success(self, get_driver, data):
        # 进行列表解包--通过两个参数去接收列表中的数据
        username, password = data
        # 传浏览器对象--再把结果返回
        login_page = LoginPage(get_driver)
        # 直接调用页面类中的 login 操作--里面需要输入两个参数(参数化传两个参数)
        login_page.login(username, password)
        # 断言结果
        success_ele = get_driver.find_element(By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')
        assert success_ele != ''
        sleep(2)

2、优化项目根目录文件 pageObject 软件包 login_page 目录文件下 login_page.py 文件内容;

复制代码
from selenium.webdriver.common.by import By
from util_tools.basePage import BasePage

# 登录页面类
class LoginPage(BasePage):
    url = 'http://localhost:8088/ecshop/user.php'
    # 用户名
    username = (By.NAME, 'username')
    # 密码
    password = (By.NAME, 'password')
    # 登录按钮
    submit = (By.NAME, 'submit')
    # 断言结果
    assert_result = (By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')

    # 登录操作
    def login(self, user_name, pass_word):
        # 打开网址
        self.open_url(self.url)
        # 输入用户名
        self.send_keys(self.username, user_name)
        # 输入密码
        self.send_keys(self.password, pass_word)
        # 点击登录按钮
        self.click(self.submit)

3、优化代码改造 test.login.py 文件中的断言结果;

4、这里可以重新写一个断言文件 ,封装成单独的一个类;(可按需自行选择)

在项目根目录文件夹下 util_tools 软件包下新建名称为 assertions.py 的 Python 文件,输入填写以下内容;

复制代码
from util_tools.basePage import BasePage

class SeleniumAssertions(BasePage):

    @staticmethod
    def assert_element_text(element, expected_text):
        """
        断言元素文本内容
        :param element: 元素
        :param expected_text: 文本
        :return:
        """
        actual_text = element.text
        assert actual_text == expected_text, f"预期文本:{expected_text},实际文本:{actual_text}"

    def assert_element_present(self, locator: tuple):
        """
        断言元素是否存在
        :param locator: 定位方式
        :return:
        """
        element = self.is_element_present(*locator)
        assert element.is_displayed(), '元素不存在!'

5、 如若单独封装此断言元素结果的话,每次需要先引用导入才可以使用;而现在页面类 class LoginPage 已经继承了 BasePage 公共方法,所以只需要写到公共方法模块中就可以了,这里删除 assertions.py 文件;(自行选择是否单独封装)

6、开始优化项目根目录文件夹 util_tools 软件包下 basePage.py 文件内容;

复制代码
# 导包
from pyxnat.core.uriutil import file_path
from selenium import webdriver
from selenium.common import NoSuchElementException
from selenium.webdriver.common.by import By
from time import sleep
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait
from config import setting
from util_tools.logs_util.recordlog import logs
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from datetime import datetime
import pytesseract
from PIL import Image

class BasePage(object):

    def __init__(self, driver):
        self.__driver = driver
        self.__wait = WebDriverWait(self.__driver, setting.WAIT_TIME)

    def window_max(self):
        self.__driver.maximize_window()

    def window_full(self):
        self.__driver.fullscreen_window()

    def screenshot(self):
        self.__driver.get_screenshot_as_png()

    def open_url(self, url):
        self.__driver.get(url)

    @property
    def current_url(self):
        return self.__driver.current_url

    @property
    def title(self):
        return self.__driver.title

    def refresh(self):
        self.__driver.refresh()

    @property
    def switch_to(self):
        return self.__driver.switch_to

    def iframe(self, frame):
        self.switch_to.frame(frame)

    def exit_iframe(self):
        self.switch_to.default_content()

    @property
    def alert(self):
        return self.__wait.until(ec.alert_is_present())

    def alert_confirm(self):
        self.alert.accept()

    def alert_cancel(self):
        self.alert.dismiss()

    def location_element(self, by, value):
        try:
            element = self.__wait.until(ec.presence_of_element_located((by, value)))
            logs.info(f"找到元素:{by}={value}")
            return element
        except Exception as e:
            logs.error(f"未找到元素:{by}={value}")
            raise e

    def location_elements(self, by, value):
        try:
            self.__wait.until(ec.presence_of_all_elements_located((by, value)))
            elements = self.__driver.find_elements(by, value)
            logs.info(f"找到元素列表:{by}={value}")
            return elements
        except Exception as e:
            logs.error(f"未找到元素列表:{by}={value}")
            raise e

    def click(self, locator: tuple, force=False):
        try:
            element = self.location_element(*locator)
            if not force:
                self.__driver.execute_script("arguments[0].click()", element)
            else:
                self.__driver.execute_script("arguments[0].click({force:true})", element)
            logs.info(f"元素被点击:{locator}")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def send_keys(self, locator: tuple, data):
        try:
            element = self.location_element(*locator)
            element.send_keys(data)
            logs.info(f"元素被输入内容:{locator},输入的内容为:{data}")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def enter(self):
        try:
            ActionChains(self.__driver).send_keys(Keys.ENTER).perform()
            logs.info("按下回车键")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def right_click(self, locator: tuple):
        try:
            element = self.location_element(*locator)
            ActionChains(driver).context_click(element).perform()
            logs.info("执行鼠标右键点击操作")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def double_click(self, locator: tuple):
        try:
            element = self.location_element(*locator)
            ActionChains(driver).double_click(element).perform()
            logs.info("执行鼠标双击操作")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def screenshots(self, image_name):
        import os
        current_time = datetime.now().strftime("%Y%m%d%H%M%S")
        file_name = f"{image_name}-{current_time}.png"
        file_path = os.path.join(setting.FILE_PATH.get('screenshot'), file_name)
        self.__driver.get_screenshot_as_file(file_path)

    def clear(self, locator: tuple):
        try:
            element = self.location_element(*locator)
            element.clear()
            logs.info("清空文本")
        except NoSuchElementException as e:
            logs.error(f"元素无法定位:{e}")
            raise e

    def ocr_captcha(self, locator: tuple):
        captcha_element = self.location_element(*locator)
        captcha_path = setting.FILE_PATH['screenshot'] + '/captcha.png'
        captcha_element.screenshot(captcha_path)
        captcha_image = Image.open(captcha_path)
        try:
            captcha_text = pytesseract.image_to_string(captcha_image)
            logs.info(f"识别到的验证码为:{captcha_text}")
            return captcha_text
        except pytesseract.pytesseract.TesseractNotFoundError:
            logs.error("找不到tesseract,这是因为pytesseract模块依赖于TesseractOCR引擎来进行图像识别!")

    def assert_is_element_present(self, locator: tuple):
        try:
            element = self.__driver.find_element(*locator)
            assert element.is_displayed(), '元素不存在'
        except NoSuchElementException as e:
            logs.error(f'元素未找到:{e}')
            raise AssertionError('元素不存在')

    def assert_title(self, expect_title):
        assert expect_title in self.title

7、继续优化 test.login.py 文件内容;

1)、通过 login_page 这个页面类的对象去调用上面步骤中刚封装好的 is_element_present() 方法;

因为页面类 LoginPage 类继承了 BasePage 基类,所以可以直接调用 BasePage 基类里面的方法;

2)、可以通过 login_page 页面类这个对象去拿到断言结果 assert_result 定位方式;

复制代码
import pytest
from selenium.webdriver.common.by import By
from time import sleep
from pageObject.login_page.login_page import LoginPage
from util_tools.handle_data.operateJson import read_json
# from util_tools.handle_data.readYaml import read_yaml
# from util_tools.handle_data.operateExcel import ExcelDataReader

class TestLogin:

    # 使用参数化实现一条测试用例跑多种场景--可迭代对象里面有多少组数据就跑多少次测试用例
    @pytest.mark.parametrize('data', read_json('./data/login.json'))
    # 用一个 data(需与上面保持一致) 参数去接收参数化数据
    def test_login_success(self, get_driver, data):
        # 进行列表解包--通过两个参数去接收列表中的数据
        username, password = data
        # 传浏览器对象--再把结果返回
        login_page = LoginPage(get_driver)
        # 直接调用页面类中的 login 操作--里面需要输入两个参数(参数化传两个参数)
        login_page.login(username, password)
        # 断言结果
        login_page.assert_is_element_present(login_page.assert_result)
        sleep(2)

8、再次优化 test.login.py 文件内容之后可以看到断言结果更加简洁明了;

9、此时可以发现 data 文件目录下 login.json 文件中的有些数据一定会存在错误的(只有账号密码都对才是正确的);

复制代码
[
  {
    "username": "admin123",
    "password": "123456"
  },
  {
    "username": "admin123456",
    "password": "123456"
  },
  {
    "username": "admin123",
    "password": "123456789"
  },
  {
    "username": "",
    "password": ""
  }
]

10、通过更改 data 文件目录下 login.json 的文件名称为 login_success.json 文件名(用于存放登录成功数据场景);

复制代码
[
  {
    "username": "admin123",
    "password": "123456"
  }
]

11、在 data 文件目录下新建一个名称为 login_failed.json 的 Json 文件(存放登录失败数据场景);

复制代码
[
  {
    "username": "admin123456",
    "password": "123456"
  },
  {
    "username": "admin123",
    "password": "123456789"
  },
  {
    "username": "admin123456",
    "password": "123456789"
  }
]

12、最后优化 test.login.py 文件,增加登录失败数据场景的测试用例,登录失败断言页面标题提示;

复制代码
import pytest
from selenium.webdriver.common.by import By
from time import sleep
from pageObject.login_page.login_page import LoginPage
from util_tools.handle_data.operateJson import read_json
# from util_tools.handle_data.readYaml import read_yaml
# from util_tools.handle_data.operateExcel import ExcelDataReader

class TestLogin:

    # 登录成功
    # 使用参数化实现一条测试用例跑多种场景--可迭代对象里面有多少组数据就跑多少次测试用例
    @pytest.mark.parametrize('data', read_json('./data/login_success.json'))
    # 用一个 data(需与上面保持一致) 参数去接收参数化数据
    def test_login_success(self, get_driver, data):
        # 进行列表解包--通过两个参数去接收列表中的数据
        username, password = data
        # 传浏览器对象--再把结果返回
        login_page = LoginPage(get_driver)
        # 直接调用页面类中的 login 操作--里面需要输入两个参数(参数化传两个参数)
        login_page.login(username, password)
        # 断言结果
        login_page.assert_is_element_present(login_page.assert_result)
        sleep(2)

    # 登录失败
    @pytest.mark.parametrize('data', read_json('./data/login_failed.json'))
    def test_login_failed(self, get_driver, data):
        username, password = data
        login_page = LoginPage(get_driver)
        login_page.login(username, password)
        login_page.assert_title('系统提示')
        sleep(2)

13、运行主函数 run.py 文件查看结果(能够成功运行登录成功、登录失败场景);

未完待续。。。

相关推荐
Li.CQ几秒前
SQL学习笔记(二)
笔记·sql·学习
Huangxy__3 分钟前
指针的补充学习
学习
Lucky高15 分钟前
Pandas库入门
python·pandas
小鸡吃米…27 分钟前
Python PyQt6教程三-菜单与工具栏
开发语言·python
Smartdaili China1 小时前
掌握Java网页抓取:技术与示例完整指南
java·网络·学习·指南·网页·住宅ip·爬虫api
Jack电子实验室1 小时前
【杭电HDU】校园网(DeepL/Srun)自动登录教程
python·嵌入式硬件·计算机网络·自动化
木头左1 小时前
二值化近似计算在量化交易策略中降低遗忘门运算复杂度
python
Jelena157795857921 小时前
Java爬虫淘宝拍立淘item_search_img拍接口示例代码
开发语言·python
郝学胜-神的一滴1 小时前
Python数据模型:深入解析及其对Python生态的影响
开发语言·网络·python·程序人生·性能优化
free-elcmacom2 小时前
机器学习进阶<8>PCA主成分分析
人工智能·python·机器学习·pca