【测开】利用界面化操作存储步骤数据,为 Selenium 自动化测试提效赋能(一)

发布于:2025-02-10 ⋅ 阅读:(47) ⋅ 点赞:(0)

开篇简介

本篇分享我设计的sqlite数据步骤表结构、以及让selenium4执行的方法;

不当之处请多多指教。

如下是qt界面,执行用例的时候,完成selenium的定位元素、鼠标事件等。

准备

1、selenium知识

selenium是可以模拟人工执行浏览器的库。

selenium4的helloworld.py,python的selenium4库已经不需要单独安装本地驱动了。

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service

# 使用 WebDriverManager 获取 ChromeDriver 的路径
driver_path = ChromeDriverManager().install()
# 使用 Service 类来传递 ChromeDriver 路径
service = Service(driver_path)


driver = webdriver.Chrome(service=service)
# 步骤1: 打开一个网页
driver.get('https://www.baidu.com/')
# 步骤2: 输入搜索词并点击搜索按钮
search_box = driver.find_element('id', 'kw')
search_box.send_keys('自动化测试')
# 步骤3: 点击提交
search_button = driver.find_element('id', 'su')
search_button.click()


# 等待几秒钟以便查看效果(可选)
import time
time.sleep(5)

# 关闭浏览器
driver.quit()

 selenium有很多操作、可以参考selenium整理
 

2. pyqt5以及QThread多线程

如下是简单的pyqt5中多线程QThread启用、以及pyqtSignal的使用

  • QThred的子类中,run方法,使用子类对象调用start()方法,就可以创建一个多线程来执行run犯法。
  • 定义一个信号,在对应的地方发射信号,最后append到文本框

import sys, time
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QVBoxLayout, QWidget,QPushButton
from PyQt5.QtCore import pyqtSignal, QObject, QThread

class SeleniumSignal(QObject):
    # 定义一个信号,用于传递字符串信息
    message = pyqtSignal(str)

class SeleniumExecutor(QThread):
    def __init__(self, signal):
        super().__init__()
        self.steps = None
        self.signal = signal
        self.excutor_person = 1

    def run(self):
        if self.steps:  # 确保有步骤需要执行
            self.excute()

    def excute(self):
        self.signal.message.emit(f"{self.excutor_person}开始执行....")
        for step in self.steps:
            action_name = step['action_name']
            self.signal.message.emit(f"{self.excutor_person}正在执行步骤: {action_name}")
            time.sleep(1)  # 模拟每个步骤之间的延迟
            # 这里可以加入实际的步骤执行逻辑
            self.signal.message.emit(f"{self.excutor_person}步骤 {action_name} 执行完成")
            time.sleep(1)  # 模拟每个步骤之间的延迟
        self.signal.message.emit(f"{self.excutor_person}所有步骤执行完成")
        self.excutor_person = 1 + self.excutor_person


    def set_steps(self,steps):
        self.steps = steps



class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Selenium Executor')
        self.resize(800,600)

        btn = QPushButton("执行")
        btn.clicked.connect(self.run_selenium)

        self.text_edit = QTextEdit(self)
        self.text_edit.setReadOnly(True)
        layout = QVBoxLayout()
        layout.addWidget(btn)
        layout.addWidget(self.text_edit)
        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

        # 创建信号对象
        self.signal = SeleniumSignal()
        self.signal.message.connect(self.update_text)
        self.signal.message.connect(lambda xx:print(xx))


        # 提前实例化SeleniumExecutor对象
        self.executor = SeleniumExecutor( self.signal)
        # # 初始化,多线程创建dirver
        self.executor.start()



    def update_text(self, message):
        self.text_edit.append(message)

    def run_selenium(self):
        # 1. 打开百度-搜索-点击
        steps = [{'id': 7, 'case_id': 2, 'step_number': 1, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'open_windows', 'action_name': '打开窗口', 'value': 'https://www.baidu.com/', 'locator_type': None, 'locator_value': None, 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 9, 'case_id': 2, 'step_number': 3, 'step_description': None, 'action_type_major': 'keyboard_operation', 'action_type_minor': 'text_input', 'action_name': '内容输入', 'value': 'ui自动化测试', 'locator_type': '0', 'locator_value': 'kw', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 10, 'case_id': 2, 'step_number': 4, 'step_description': None, 'action_type_major': 'mouse_operation', 'action_type_minor': 'mouse_click', 'action_name': '左键点击', 'value': None, 'locator_type': '0', 'locator_value': 'su', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 8, 'case_id': 2, 'step_number': 6, 'step_description': None, 'action_type_major': 'wait_operation', 'action_type_minor': 'force_wait', 'action_name': '强制等待', 'value': None, 'locator_type': None, 'locator_value': None, 'show_wait_time': 2, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}]

        self.executor.set_steps(steps)
        self.executor.start()       # 启动线程start() 方法会在一个新的线程中运行 run() 方法,从而避免阻塞主线程。


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

3. google浏览器

        以下代码暂时没有兼容其他浏览器。

一、步骤数据结构、获取

步骤表ddl

CREATE TABLE test_steps (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                case_id INTEGER NOT NULL,
                step_number INTEGER NOT NULL,
                step_description TEXT ,
                action_type_major TEXT,
                action_type_minor TEXT,
                action_name TEXT,
                value TEXT,
                locator_type TEXT,
                locator_value TEXT,
                show_wait_time INTEGER,
                iswait_find INTEGER DEFAULT 1,
                keyword_value TEXT,
                locator_type2 TEXT,
                locator_value2 TEXT,
                FOREIGN KEY (case_id) REFERENCES test_cases(id)
            );

字段介绍

 

keyword_value是js操作中,获取元素的某个属性的,属性名称值。

locator_type2、locator_value2是鼠标拖拽操作时,需要第二个元素定位值。

两个用例数据

如下是通过工具、存好的测试用例的数据、我从sqlite获取出来了

        # 1. 打开百度-搜索-点击
        steps = [{'id': 7, 'case_id': 2, 'step_number': 1, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'open_windows', 'action_name': '打开窗口', 'value': 'https://www.baidu.com/', 'locator_type': None, 'locator_value': None, 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 9, 'case_id': 2, 'step_number': 3, 'step_description': None, 'action_type_major': 'keyboard_operation', 'action_type_minor': 'text_input', 'action_name': '内容输入', 'value': 'ui自动化测试', 'locator_type': '0', 'locator_value': 'kw', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 10, 'case_id': 2, 'step_number': 4, 'step_description': None, 'action_type_major': 'mouse_operation', 'action_type_minor': 'mouse_click', 'action_name': '左键点击', 'value': None, 'locator_type': '0', 'locator_value': 'su', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 8, 'case_id': 2, 'step_number': 6, 'step_description': None, 'action_type_major': 'wait_operation', 'action_type_minor': 'force_wait', 'action_name': '强制等待', 'value': None, 'locator_type': None, 'locator_value': None, 'show_wait_time': 2, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}]
        # 2. 切换句柄,并执行js
        steps =[{'id': 14, 'case_id': 4, 'step_number': 1, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'open_windows', 'action_name': '打开窗口', 'value': 'https://www.baidu.com/', 'locator_type': None, 'locator_value': None, 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}, 
                {'id': 15, 'case_id': 4, 'step_number': 2, 'step_description': None, 'action_type_major': 'mouse_operation', 'action_type_minor': 'mouse_click', 'action_name': '左键点击', 'value': None, 'locator_type': '1', 'locator_value': 'ul  > li:nth-of-type(5) .title-content-title', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}, 
                {'id': 16, 'case_id': 4, 'step_number': 3, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'switch_window_by_handleindex', 'action_name': '句柄索引切换窗口', 'value': '-1', 'locator_type': None, 'locator_value': None, 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}, 
                {'id': 17, 'case_id': 4, 'step_number': 4, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'execute_js', 'action_name': '执行js', 'value': 'window.scrollTo(0, document.body.scrollHeight);', 'locator_type': '0', 'locator_value': '', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}]

二、selenium执行的实现

  • 1.QThread子类中初始化,先获取到 self.driver
  • 2.把操作步骤放到run()中实现,保障执行用例时,是多线程执行,不影响主线程

实现逻辑:先获取到所有步骤,然后看每步的action_type_minor(操作)是什么,再来做对应的selenium操作。

如:

import sys, time
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QVBoxLayout, QWidget,QPushButton
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from selenium import webdriver
from selenium.webdriver import ActionChains
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.webdriver.chrome.options import Options
import time
class SeleniumSignal(QObject):
    # 定义一个信号,用于传递字符串信息
    message = pyqtSignal(str)

    load_complete = pyqtSignal()

class SeleniumExecutor(QThread):
    def __init__(self, signal):
        super().__init__()
        self.steps = None
        self.signal = signal

        # self.signal.message.emit(f"正在启动浏览器....")
        # 初始化WebDriver
        chrome_options = Options()
        # chrome_options.add_argument("--headless")  # 启动无头模式(没有浏览器窗口)
        # 创建 WebDriver 实例并启动浏览器
        self.driver = webdriver.Chrome( options=chrome_options)
        self.signal.message.emit(f"浏览器启动完成.")
        self.signal.load_complete.emit()

    def run(self):
        if self.steps:  # 确保有步骤需要执行
            self.excute(self.steps)

        # else:
        #     self.signal.message.emit("没有任务需要执行")

    def excute(self,steps):
        print("打印dirver",   self.driver)
        if steps ==[]:
            self.signal.message.emit(f"该用例未添加步骤")

        for step in steps:
            action_type_minor = step['action_type_minor']
            value = step['value']
            locator_type = step['locator_type']
            locator_value = step['locator_value']
            show_wait_time = step['show_wait_time']
            iswait_find = step['iswait_find']
            keyword_value = step['keyword_value']
            locator_type2 = step['locator_type2']
            locator_value2 = step['locator_value2']

            # if show_wait_time:
            #     WebDriverWait(  self.driver, show_wait_time).until(
            #         EC.presence_of_element_located((By.ID, locator_value))
            #     )

            if action_type_minor == 'open_windows':
                try:
                    self.driver.get(value)
                    print(f"Opened URL: {value}")
                    self.signal.message.emit(f"Opened URL: {value}")
                except:
                    self.signal.message.emit(f"无法打开窗口")

            # 输入
            elif action_type_minor == 'text_input':
                try:
                    if iswait_find:     # 显示等待
                        element = self.wait_find_element(self.driver, locator_type, locator_value)
                    else:
                        element = self.find_element(self.driver, locator_type, locator_value)
                    element.send_keys(value)
                    self.signal.message.emit(
                        f"在{locator_value}输入文本'{value}'  located by {locator_type}")
                except:
                    self.signal.message.emit(f"无法定位")

            # 鼠标左键点击
            elif action_type_minor == 'mouse_click':
                try:
                    if iswait_find:  # 显示等待
                        element = self.wait_find_element(self.driver, locator_type, locator_value)
                    else:
                        element = self.find_element(self.driver, locator_type, locator_value)
                    element.click()
                    # print(f"Clicked on element located by {locator_type}={locator_value}")
                    self.signal.message.emit(f"在{locator_value}点击 element located by {locator_type}")
                except:
                    self.signal.message.emit(f"无法定位")

            elif action_type_minor == 'maximize_window':
                self.driver.maximize_window()
                self.signal.message.emit(f"窗口最大化")

            elif action_type_minor == 'minimize_window':
                self.driver.minimize_window()
                self.signal.message.emit(f"窗口最小化")

            # 浏览器后退
            elif action_type_minor == 'browser_backward':
                self.driver.back()
                self.signal.message.emit(f"浏览器后退")
            # 浏览器前进
            elif action_type_minor == 'browser_forward':
                self.driver.forward()
                self.signal.message.emit(f"浏览器前进")

            #浏览器刷新
            elif action_type_minor == 'browser_refresh':
                self.driver.refresh()
                self.signal.message.emit(f"浏览器刷新")


            # 强制等待
            elif action_type_minor == 'force_wait':
                time.sleep(show_wait_time)
                self.signal.message.emit(f"强制等待了{show_wait_time}秒")

            # 获取元素文本
            elif action_type_minor == 'element_text':
                try:
                    if iswait_find:  # 显示等待
                        element = self.wait_find_element(self.driver, locator_type, locator_value)
                    else:
                        element = self.find_element(self.driver, locator_type, locator_value)
                    text = element.text
                    self.signal.message.emit(f"{locator_type}方式获取元素{locator_value}的文本为:{text}'")
                except:
                   self.signal.message.emit(f"无法定位")

            # 获取元素属性
            elif action_type_minor == 'element_attribute':
                try:
                    if iswait_find:  # 显示等待
                        element = self.wait_find_element(self.driver, locator_type, locator_value)
                    else:
                        element = self.find_element(self.driver, locator_type, locator_value)
                    text = element.get_attribute(keyword_value)
                    self.signal.message.emit(f"{locator_type}方式获取元素{locator_value}的{keyword_value}属性为:{text}'")
                except:
                   self.signal.message.emit(f"无法定位")

            elif action_type_minor == 'switch_window_by_handleindex':
                try:
                    window_handles=self.driver.window_handles
                    self.driver.switch_to.window(window_handles[int(value)])
                    self.signal.message.emit(f"切换窗口到索引为{value}的窗口")
                except:
                    self.signal.message.emit(f"无法切换窗口到索引为{value}的窗口")

            # 执行js
            elif action_type_minor == 'execute_js':
                try:
                    if "arguments[0]" in value:
                        if iswait_find:  # 显示等待
                            element = self.wait_find_element(self.driver, locator_type, locator_value)
                        else:
                            element = self.find_element(self.driver, locator_type, locator_value)
                        result = self.driver.execute_script(value,element)
                        self.signal.message.emit(f"执行js11:{value}返回值{result}")
                    else:
                        result = self.driver.execute_script(value)
                        self.signal.message.emit(f"执行js22:{value}返回值{result}")
                except:
                    self.signal.message.emit(f"无法执行js:{value}")

            # 鼠标操作-移入悬停
            elif action_type_minor == 'mouse_hover':
                try:
                    if iswait_find:  # 显示等待
                        element = self.wait_find_element(self.driver, locator_type, locator_value)
                    else:
                        element = self.find_element(self.driver, locator_type, locator_value)
                    ActionChains(self.driver).move_to_element(element).perform()
                    self.signal.message.emit(f"鼠标悬停到{locator_value}")
                except:
                   self.signal.message.emit(f"无法定位")

            # 鼠标操作-双击
            elif action_type_minor == 'double_click':
                try:
                    if iswait_find:  # 显示等待
                        element = self.wait_find_element(self.driver, locator_type, locator_value)
                    else:
                        element = self.find_element(self.driver, locator_type, locator_value)
                    ActionChains(self.driver).double_click(element).perform()
                    self.signal.message.emit(f"鼠标双击到{locator_value}")
                except:
                    self.signal.message.emit(f"无法定位")

            # 鼠标操作-拖动
            elif action_type_minor == "drag_and_drop":
                try:
                    if iswait_find:  # 显示等待
                        element1 = self.wait_find_element(self.driver, locator_type, locator_value)
                        element2 = self.wait_find_element(self.driver, locator_type2, locator_value2)
                    else:
                        element1 = self.find_element(self.driver, locator_type, locator_value)
                        element2 = self.find_element(self.driver, locator_type2, locator_value2)
                    ActionChains(self.driver).drag_and_drop(element1, element2).perform()
                    self.signal.message.emit(f"鼠标拖动{locator_value}到{locator_value2}")
                except:
                    self.signal.message.emit(f"无法定位")

    def set_steps(self, steps):
        self.steps = steps  # 设置需要执行的步骤


    def find_element(self, driver, locator_type, locator_value):
        if locator_type == '0':  # Assuming '0' means ID
            return driver.find_element(By.ID, locator_value)
        # elif locator_type == '1':  # Assuming '1' means NAME
        #     return driver.find_element(By.NAME, locator_value)
        elif locator_type == '2':  # Assuming '2' means XPATH
            return driver.find_element(By.XPATH, locator_value)
        elif locator_type == '1':  # Assuming '3' means CSS_SELECTOR
            return driver.find_element(By.CSS_SELECTOR, locator_value)
        else:
            raise ValueError(f"Unknown locator type: {locator_type}")

    # 10s的显示等待的查找元素
    def wait_find_element(self, driver, locator_type, locator_value):
        if locator_type == '0':  # Assuming '0' means ID
            return WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, locator_value)))
        elif locator_type == '1':  # Assuming '3' means CSS_SELECTOR
            return WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, locator_value)))
        elif locator_type == '2':  # Assuming '2' means XPATH
            return WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, locator_value)))
        else:
            raise ValueError(f"Unknown locator type: {locator_type}")


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Selenium Executor')
        self.resize(800,600)

        btn = QPushButton("执行")
        btn.clicked.connect(self.run_selenium)

        self.text_edit = QTextEdit(self)
        self.text_edit.setReadOnly(True)
        layout = QVBoxLayout()
        layout.addWidget(btn)
        layout.addWidget(self.text_edit)
        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

        # 创建信号对象
        self.signal = SeleniumSignal()
        self.signal.message.connect(self.update_text)
        self.signal.message.connect(lambda xx:print(xx))


        # 提前实例化SeleniumExecutor对象
        self.executor = SeleniumExecutor( self.signal)
        # 初始化,多线程创建dirver
        self.executor.start()


    def update_text(self, message):
        self.text_edit.append(message)

    def run_selenium(self):
        # 1. 打开百度-搜索-点击
        steps = [{'id': 7, 'case_id': 2, 'step_number': 1, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'open_windows', 'action_name': '打开窗口', 'value': 'https://www.baidu.com/', 'locator_type': None, 'locator_value': None, 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 9, 'case_id': 2, 'step_number': 3, 'step_description': None, 'action_type_major': 'keyboard_operation', 'action_type_minor': 'text_input', 'action_name': '内容输入', 'value': 'ui自动化测试', 'locator_type': '0', 'locator_value': 'kw', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 10, 'case_id': 2, 'step_number': 4, 'step_description': None, 'action_type_major': 'mouse_operation', 'action_type_minor': 'mouse_click', 'action_name': '左键点击', 'value': None, 'locator_type': '0', 'locator_value': 'su', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                 {'id': 8, 'case_id': 2, 'step_number': 6, 'step_description': None, 'action_type_major': 'wait_operation', 'action_type_minor': 'force_wait', 'action_name': '强制等待', 'value': None, 'locator_type': None, 'locator_value': None, 'show_wait_time': 2, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}]
        # 2. 切换句柄,并执行js
        steps =[{'id': 14, 'case_id': 4, 'step_number': 1, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'open_windows', 'action_name': '打开窗口', 'value': 'https://www.baidu.com/', 'locator_type': None, 'locator_value': None, 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                {'id': 15, 'case_id': 4, 'step_number': 2, 'step_description': None, 'action_type_major': 'mouse_operation', 'action_type_minor': 'mouse_click', 'action_name': '左键点击', 'value': None, 'locator_type': '1', 'locator_value': 'ul  > li:nth-of-type(5) .title-content-title', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                {'id': 16, 'case_id': 4, 'step_number': 3, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'switch_window_by_handleindex', 'action_name': '句柄索引切换窗口', 'value': '-1', 'locator_type': None, 'locator_value': None, 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None},
                {'id': 17, 'case_id': 4, 'step_number': 4, 'step_description': None, 'action_type_major': 'browser_operation', 'action_type_minor': 'execute_js', 'action_name': '执行js', 'value': 'window.scrollTo(0, document.body.scrollHeight);', 'locator_type': '0', 'locator_value': '', 'show_wait_time': None, 'iswait_find': 1, 'keyword_value': None, 'locator_type2': None, 'locator_value2': None}]

        self.executor.set_steps(steps)
        self.executor.run()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

代码运行后,就可以打开google浏览器,点击执行,就会执行其中的步骤,同时去操作浏览器 


网站公告

今日签到

点亮在社区的每一天
去签到