Selenium Web自动化测试框架实践

2023-10-30

功能实现

  • 自动运行用例
  • 自动生成测试报告
  • 自动断言与截图
  • 自动将最新测试报告发送到指定邮箱
  • 自动生成测试报告
  • 数据,页面元素、测试用例分离
  • 执行日志、分布式执行
  • 配置化文件、元素、数据

实践功能

https://passport.csdn.net/login CSDN登录页面

项目架构

在这里插入图片描述

浏览器driver定义

from common.readFile import ReadFile
from common.logger import Logger
from selenium import webdriver

logger = Logger()

from selenium.webdriver import Remote

class Browser():

    def __init__(self):
        config = ReadFile()
        self.browser = config.readConfig("Browser", "browser")
        self.host = config.readConfig("host","host")
        logger.info("You had select {} host {} browser.".format(self.host,self.browser))

    def driver(self):
        """
        启动浏览器驱动
        :return: 返回浏览器驱动URL
        """
        try:
            # driver = webdriver.Chrome()
            driver = Remote(command_executor='http://' + self.host + '/wd/hub',
                            desired_capabilities={ 'platform': 'ANY',
                                                   'browserName': self.browser,
                                                   'version': "",
                                                   'javascriptEnabled': True
                                                }
                            )
            return driver
        except Exception as msg:
            print("驱动异常-> {0}".format(msg))

用例运行前后的环境准备工作

import unittest
from common.driver import Browser

class StartEnd(unittest.TestCase):
    def setUp(self):
        self.driver = Browser().driver()
        self.driver.implicitly_wait(10)
        self.driver.maximize_window()

    def tearDown(self):
        self.driver.quit()

工具方法模块

主要封装一些公共的方法如:截图,查找最新报告

import time
from selenium import webdriver

import os,sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))

from config import setting

def inser_img(driver):
    # 指定截图存放的根目录路径
    screen_dir = setting.TEST_REPORT + '/imges/'
    rq = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
    screen_name = screen_dir + rq + '.png'
    driver.get_screenshot_as_file(screen_name)
    print('screenshot:' + screen_name)

#查找最新的测试报告
def latest_report(report_dir):
    lists = os.listdir(report_dir)
    lists.sort(key=lambda fn: os.path.getatime(report_dir + '\\' + fn))
    file = os.path.join(report_dir, lists[-1])
    return file

def latest_report_img(report_dir):
    lists = os.listdir(report_dir)
    lists.sort(key=lambda fn: os.path.getatime(report_dir + '\\' + fn))
    file = os.path.join(report_dir, lists[-1])
    return file

Pageobject页面对象封装

基础页面封装类

import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from common.logger import Logger
from common.readFile import ReadFile

logger = Logger()

class BasePage():
    "定义一个页面基类,让所有页面都继承这个类,封装一些常用的页面操作方法到这个类"

    def __init__(self, driver):
        self.driver = driver
        config = ReadFile()
        self.baseurl = config.readConfig("BaseUrl", "url")

    def open_url(self, url):
        self.driver.get(self.baseurl + url)

    # 退出浏览器
    def quit_browser(self):
        self.driver.quit()

    # 浏览器前进操作
    def forward(self):
        self.driver.forward()

    # 浏览器后退操作
    def back(self):
        self.driver.back()

    # 隐式等待
    def wait(self, seconds):
        self.driver.implicitly_wait(seconds)

    # 查找元素
    def find_element(self, selector):
        selector_by = selector['find_type']
        selector_value = selector['element_info']

        try:
            if selector_by == 'id':
                el = self.driver.find_element_by_id(selector_value)
            elif selector_by == "n" or selector_by == 'name':
                el = self.driver.find_element_by_name(selector_value)
            elif selector_by == 'cs' or selector_by == 'css_selector':
                el = self.driver.find_element_by_css_selector(selector_value)
            elif selector_by == 'cn' or selector_by == 'classname':
                el = self.driver.find_element_by_class_name(selector_value)
            elif selector_by == "lt" or selector_by == 'link_text':
                el = self.driver.find_element_by_link_text(selector_value)
            elif selector_by == "plt" or selector_by == 'partial_link_text':
                el = self.driver.find_element_by_partial_link_text(selector_value)
            elif selector_by == "tn" or selector_by == 'tag_name':
                el = self.driver.find_element_by_tag_name(selector_value)
            elif selector_by == "x" or selector_by == 'xpath':
                el = self.driver.find_element_by_xpath(selector_value)
            elif selector_by == "ss" or selector_by == 'selector_selector':
                el = self.driver.find_element_by_css_selector(selector_value)
            else:
                raise NameError("Please enter a valid type of targeting elements.")
        except NoSuchElementException  :
            logger.error("{0}页面中未能找到{1}元素".format(self, selector_value))

        return el

    # 输入
    def input(self, selector, text):
        el = self.find_element(selector)
        try:
            el.clear()
            el.send_keys(text)
            logger.info("Had type \' %s \' in inputBox" % text)
        except NameError as e:
            logger.error("Failed to type in input box with %s" % e)

    # 点击
    def click(self, selector):
        el = self.find_element(selector)
        try:
            logger.info("The element \' %s \' was clicked." % el.text)
            el.click()
        except NameError as e:
            logger.error("Failed to click the element with %s" % e)

    @staticmethod
    def sleep(seconds):
        time.sleep(seconds)
        logger.info("Sleep for %d seconds" % seconds)

    def get_text(self,selector):
        el = self.find_element(selector)
        try:
            return el.text
        except NameError as e:
            logger.error("Failed to text the element with %s" % e)

    def switch_frame(self, selector):
        """
        多表单嵌套切换
        :param loc: 传元素的属性值
        :return: 定位到的元素
        """
        try:
            el = self.find_element(selector)
            return self.driver.switch_to_frame(el)
        except NoSuchElementException as e:
            logger.error("查找iframe异常-> {0}".format(e))

    def switch_windows(self, selector):
        """
        多窗口切换
        :param loc:
        :return:
        """
        try:
            el = self.find_element(selector)
            return self.driver.switch_to_window(el)
        except NoSuchElementException as e:
            logger.error("查找窗口句柄handle异常-> {0}".format(e))

    def switch_alert(self):
        """
        警告框处理
        :return:
        """
        try:
            return self.driver.switch_to_alert()
        except NoSuchElementException as e:
            logger.error("查找alert弹出框异常-> {0}".format(e))

LoginPage.py —— CNDS登录页面

from  pageObject.basePage import *
from selenium import webdriver
from common.readFile import ReadFile
from config import setting

login_el = ReadFile().readYaml(setting.TEST_Element_YAML + '/' + 'login.yaml')
data = ReadFile().readYaml(setting.TEST_DATA_YAML + '/' + 'login_data.yaml')

class CndsPage(BasePage):
    '''登录页面'''

    url = '/login'

    # 定位器,通过元素属性定位元素对象
    #选择账号密码登录
    chanlelogin_loc = login_el['testcase'][0]
    # 账号输入框
    username_loc = login_el['testcase'][1]
    # 密码输入框
    pwd_loc = login_el['testcase'][2]
    # 单击登录
    login_accout_loc = login_el['testcase'][3]

    def accout_login(self,accout,passwd):
        self.open_url(self.url)
        self.click(self.chanlelogin_loc)
        self.input(self.username_loc,accout)
        self.input(self.pwd_loc,passwd)
        self.click(self.login_accout_loc)

    # 定位器,通过元素属性定位检查项元素对象
    user_login_success_loc = login_el['check'][0]
    accout_id_loc = login_el['check'][1]
    accout_pawd_error_loc = login_el['check'][2]

    # 账号或密码错误提示
    def accout_passwd_error(self):
        return self.get_text(self.accout_pawd_error_loc)

    # 登录成功,跳转到个人资料页,获取用户名
    def get_account(self):
        self.click(self.user_login_success_loc)
        time.sleep(2)

    def user_login_success(self):
        return self.find_element(self.accout_id_loc).text

组织测试用例

  • 用户名密码正确点击登录
  • 用户名正确,密码错误点击登录
import unittest
from common import function,myUnit,readFile
from pageObject.loginPage import CndsPage
from time import sleep
from common.logger import Logger
from config import setting
import ddt

log = Logger()

testData= readFile.ReadFile().readYaml(setting.TEST_DATA_YAML + '/' + 'login_data.yaml')

@ddt.ddt
class LoginTest(myUnit.StartEnd):
    # @unittest.skip('skip this case')
    """CNDS登录测试"""
    def user_login_verify(self,account,passwd):
        """
        用户登录
        :param :account 账号
        :param passwd: 密码
        :return:
        """
        CndsPage(self.driver).accout_login(account,passwd)

    @ddt.data(*testData)
    def test_login_normal(self,datayaml):
        log.info("test_login1_normal is start run...")
        self.user_login_verify(datayaml['data']['accout'],datayaml['data']['passwd'])
        sleep(3)
        #断言与截屏
        po = CndsPage(self.driver)
        if datayaml['screenshot'] == 'login_success':
            po.get_account()
            function.inser_img(self.driver)
            self.assertEqual(po.user_login_success(), datayaml['check'][0], "登录成功,返回实际结果是->: {0}".format(po.user_login_success()))
        else:
            function.inser_img(self.driver)
            self.assertEqual(po.accout_passwd_error(), datayaml['check'][0],"登录失败,返回实际结果是->: {0}".format(po.accout_passwd_error()))
        print("test_login1_normal is test end!")

执行测试用例

import unittest
from  common.function import latest_report
from  common.sendMail import *
from config import setting
from thridLib.HTMLTestRunner import HTMLTestRunner
import time
import os,sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
report_dir = setting.TEST_REPORT + '/report/'

def add_case(test_path=setting.TEST_DIR):
    discover = unittest.defaultTestLoader.discover(test_path, pattern="test*.py")
    return discover

def run_case(all_case,result_path=report_dir):
    print("start run testcase...")
    now = time.strftime("%Y-%m-%d %H_%M_%S")
    report_name = result_path + '/' + now + 'result.html'
    print("start write report...")

    #HTMLTestRunner测试报告
    with open(report_name, 'wb') as f:
        runner = HTMLTestRunner(stream=f, title='测试报告', description='用例执行情况')  # 定义测试报告
        runner.run(all_case)  # 执行测试用例
    f.close()

    print("find latest report...")
    # 查找最新的测试报告
    report = latest_report(result_path)
    # 邮件发送报告
    print("send email report...")
    send_mail(report)
    print("test end!")

if __name__ == '__main__':
    cases = add_case()
    run_case(cases)

测试报告

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Selenium Web自动化测试框架实践 的相关文章

  • BAT大厂陆续裁员,2023,还能入职大厂吗?

    2022年 陆续传出了京东 字节 阿里 腾讯等大公司裁员的消息 让人有点看不懂的是 裁员过后 却是各大公司继续招人的消息 一边招一边裁 这是什么 玩法 鲶鱼效应告诉我们 活力来源于竞争 来自于压力和挑战 若不淘汰那些学习能力弱 技术提升慢
  • 【软件测试】快速入行软件测试

    快速入行软件测试 1 初识软件测试 软件测试的前景与现状 软件测试越来越被企业重视 人才稀缺 市场需求扩大 软件测试人员的职业生涯规划 技术型路线 自动化测试工程师 性能测试工程师 安全测试工程师等 管理型路线 测试主管 测试经理 测试总监
  • 是面试官放水,还是公司实在是太缺人?这都没挂,腾讯原来这么容易进···

    本人211非科班 之前在字节和腾讯实习过 这次其实没抱着什么特别大的希望投递 没想到腾讯可以再给我一次机会 还是挺开心的 本来以为有个机会就不错啦 没想到能成功上岸 在这里要特别感谢帮我内推的同学 中间投递比较曲折 是他帮了我很多 非常负责
  • LoadRunner性能测试基本步骤

    前言 本文旨在指导初学者使用LoadRunner进行基础的性能测试 我们在接到一个性能测试任务的时候 需要从以下几点考虑 我们的测试对象是什么 测试要求是什么 测试环境怎么部署的 业务规模如何 哪些业务点是客户最关注的等等 下面将从性能测试
  • 字节原来这么容易进,是面试官放水,还是公司实在是太缺人?

    本人211非科班 之前在字节和腾讯实习过 这次其实没抱着什么特别大的希望投递 没想到字节可以再给我一次机会 还是挺开心的 本来以为有个机会就不错啦 没想到能成功上岸 在这里要特别感谢帮我内推的同学 中间投递比较曲折 是他帮了我很多 非常负责
  • 媲美postman?这款国产测试工具你知道吗

    没有测试数据的用例就像一盘散沙 跑两步就跑不动了 没有测试数据 所谓的功能测试和性能测试全都是无米之炊 但我发现一个蛮诡异的事情 就是行业内很少会有人去强调测试数据的重要性 甚至市面上都没有人在做测试数据这门生意 至今测试er造测试数据还是
  • 接口测试面试题含答案

    1 解释一下正向和逆向测试 正向测试 针对接口设计预期的功能和行为 验证接口是否按照预期工作 逆向测试 针对错误输入 不合理的条件或非预期的使用方式 验证接口是否能够适当地处理这些情况并提供合理的错误处理 2 什么是API和Web服务 AP
  • Paramiko远程操作Linux服务器

    在日常工作中我们经常会跟Linux打交道 对于测试同学来说 使用Linux的场景还是比较多的 比如 搭建测试环境 查看日志信息 修改配置文件 监控服务资源等 本篇将介绍一个Python的第三方库Paramiko 使用Paramiko 我们可
  • fiddler抓取,Android真机测试

    1 配置Fiddler抓取并解密HTTPS包 Fiddler默认是不抓取HTTPS包的 需要进行相应的配置 打开Fiddler 选择 Tools gt Fiddler Options 2 在弹出的对话框中选择 HTTPS 选项卡 3 勾选
  • 【测试开发】阿里十年总结之软件测试的价值

    阿里十年总结之软件测试的价值 1 前言 2 质量是什么 2 1 质量是一种奢侈品 2 2 质量是产品的特性 2 3 质量的重要性取决于业务 3 测试能给业务带来什么 3 1 为什么需要测试 3 2 从质量保障到研发效能 4 测试团队如何去突
  • python单元测试框架(测试固件、批量执行)

    python测试框架 在Python语言中应用最广泛的单元测试框架是unittest和pytest unittest属于标准库 只要安装了Python解释器后就可以直接导入使用了 pytest是第三方的库 需要单独的安装 1 白盒测试原理
  • 【python】深入了解Selenium-PageObject

    1 PageObject 定义 Page Object 简称PO 模式 是Selenium实战中最为流行 并且是自动化测试中最为熟悉和推崇的一种设计模式 在设计自动化测试时 把页面元素和元素的操作方法按照页面抽象出来 分离成一定的对象 然后
  • 一些测试案例

    一些测试案例 微信发红包测试用例 功能 可移植性 性能 界面 安全性 易用性 水杯的测试用例 功能 界面 性能 安全性 兼容性 易用性 可移植性 抗震性 电梯的测试用例 功能测试 界面测试 易用性测试 安全性测试 性能测试 兼容性测试 淘宝
  • robot framework 初学--打开浏览器

    在TesterHome看到一篇文章后 跟着做了一遍 源地址https testerhome com topics 10447 这里是我个人操作之后的一些疑问和解决方法 1 Robot是否是以键值对的形式存在呢 A 我实在是太天真了 只是看到
  • 公司新招了个字节拿36K的人,让我见识到了什么才是测试扛把子......

    5年测试 应该是能达到资深测试的水准 即不仅能熟练地开发业务 而且还能熟悉项目开发 测试 调试和发布的流程 而且还应该能全面掌握数据库等方面的技能 如果技能再高些的话 甚至熟悉分布式组件等高级技能 或者说 做个项目小组长 管个3 4号人 应
  • 2023备战金三银四,Python自动化软件测试面试宝典合集(五)

    接上篇 八 抓包与网络协议 8 1 抓包工具怎么用 我原来的公司对于抓包这块 在 App 的测试用得比较多 我们会使用 fiddler 抓取数据检查结果 定 位问题 测试安全 制造弱网环境 如 抓取数据通过查看请求数据 请求行 请求报头 请
  • APK 逆向工程 - 解析 apk 基本信息和方法调用图

    导读 在 Android 开发中 我们很少使用 Android 逆向去分析 apk 文件的 但是作为一个测试人员 我们要对这个 apk 文件进行一系列的分析 审核 测试 这篇文章讲解如何解析一个 apk 文件 主要从下面几方面介绍 解析前准
  • 【测试】三张图解读 CI/CD

    文章目录 前言 CI CD 的概述 CI持续集成 Continuous Integration 持续集成小结 CD 持续交付 Continuous Delivery 持续交付小结 CD 持续部署 Continuous Deployment
  • Rabbitmq消息的有序性、消息不丢失、不被重复消费

    如何保证消息的顺序性 如图所示 RabbitMQ保证消息的顺序性 就是拆分多个 queue 每个 queue 对应一个 consumer 消费者 就是多一些 queue 而已 确实是麻烦点 或者就一个 queue 但是对应一个 consum
  • 公司刚上市就来了个从字节拿28K的人,让我见识到了什么才是测试天花板···

    5年测试 应该是能达到资深测试的水准 即不仅能熟练地开发业务 而且还能熟悉项目开发 测试 调试和发布的流程 而且还应该能全面掌握数据库等方面的技能 如果技能再高些的话 甚至熟悉分布式组件等高级技能 或者说 做个项目小组长 管个3 4号人 应

随机推荐

  • unity按钮实现人物变大效果

    unity按钮实现人物变大效果 游戏里面模型变大效果的实现 如下动态图所示 点我下载 https download csdn net download weixin 43474701 71975042
  • java 通过pdf模板,生成PDF,并下载到本地

    注意 本例子是从向模板定义的变量赋值 而不是从无到有的来生成pdf 直接就能用 maven依赖
  • 精通MySQL之架构篇

    今天给大家分享的是大数据开发基础部分MySQL的第一篇 老刘讲点和别人不一样的内容 众多伙伴都知道MySQL的基础知识以及使用 但是对里面的原理知道的不多 咱们学知识只看表面绝对是不行的 所以老刘争取把MySQL的架构知识给大家讲明白 My
  • Shell编程之echo命令

    Shell 的 echo 指令与 PHP 的 echo 指令类似 都是用于字符串的输出 命令格式 echo string 您可以使用echo实现更复杂的输出格式控制 1 显示普通字符串 echo It is a test 这里的双引号完全可
  • Vue3 (computed函数,watch函数,watchEffect函数)

    1 computed函数 与vue2中computed配置功能一致 变化 需要引入 组合式的API
  • 树莓派第一讲:入门那些事(系统烧录、外设连接)

    目录 基本了解 系统烧录 连接外设 基本了解 树莓派4B是一款单板计算机 采用ARM架构处理器 配备4GB内存 Gigabit以太网口 多个USB接口 HDMI输出接口等 它具备1 5Ghz运行的64位四核处理器 最高支持以60fps速度刷
  • 因果推断 - 基础知识

    目录 因果关系之梯 因果图的路径结构 阻断 d 分离 混杂 结构因果模型 SCM 版权 转载前请联系作者获得授权 声明 部分内容出自因果关系之梯 已获得原作者授权 参考书籍 The Book of Why Judea Pearl 因果关系之
  • 什么是回调函数Callback----自己的一点理解

    何为回调函数 若把函数的指针作为函数参数传递给一个函数 当这个指着被用来调用它所指向的函数时 我们将该指针所指向的函数称为回调函数 回调函数与普通函数最大区别在于函数的调用 对普通函数而言 函数实现者可以直接拿来用 可以直接将它放在main
  • 金山逍遥网 sersync 服务器实时镜像同步方案

    金山逍遥网 sersync 服务器实时镜像同步方案 1 sersync rsync原理 2 inotify和sersync同步的区别 3 配置sersync rsync实现实时同步 2台centos7 4 一台装sersync一台装rsyn
  • 『网络安全』蜜罐到蜜网入门指南(三)蜜罐内部组成分析

    原创不易 点个赞呗 如果喜欢 关注 收藏不迷路 前言 大家好 网络安全 蜜罐到蜜网入门指南 进入第三篇 通过前面的内容 我们知道了什么是蜜罐以及蜜罐的作用和分类等 点击下方链接 可快速查看 相关文章 网络安全 蜜罐到蜜网入门指南 一 蜜罐初
  • php执行命令的函数和方法

    PHP提供4个专门的执行外部命令的函数 exec system passthru shell exec 1 exec 原型 string exec string command array output int return var 说明
  • Win10+Ubuntu双系统 使用EasyUEFI修复Ubuntu引导启动项

    某同事安装了Win10 Ubuntu双系统 有一天电脑突然坏了开不了机 把硬盘拆下放到别的机器上 发现是直接进入Win10系统 而不是grub选择界面 进F12也找不到ubuntu的启动项 最开始我先使用 Win10 Ubuntu双系统修复
  • 在项目中添加天气预报功能

    查看当地的天气情况 调用七日的天气预报情况 天气预报 注册账号获取appid和APPSecret使用 https www tianqiapi com api 图标可以直接在网站上下载
  • CoreDNS 惊现诡异 bug,导致服务大面积中断

    Sealos 公众号已接入了 GPT 4 完全免费 欢迎前来调戏 原文链接 https juejin cn post 7277471908417110053 我是 LEE 老李 一个在 IT 行业摸爬滚打 17 年的技术老兵 事件背景 某天
  • Flutter开发(十二)—— 页面跳转与返回

    示例代码 以下代码展示页面跳转与返回 抛开所有复杂因素 只展示最简单的跳转 第一个页面 点击按钮时onPressed 进行相应 通过 Navigator push 和 MaterialPageRoute 进行页面跳转功能实现 第二个页面 点
  • python中百分数如何表达_Python转换百分数表示法

    YAML中任何以百分号结尾的数字序列都将 通常作为字符串标量加载 因为 不会 标量匹配任何其他模式 尤其是不匹配整数的模式 或浮动 当然 您可以递归地遍历数据结构 从YAML和patch加载 但是如果在 数据结构 用于构造特定对象 递归到这
  • passwd: Authentication token manipulation error----linux

    author skatetime 2009 04 11 更改centos4 7 linux的root密码 报 passwd Authentication token manipulation error 如下所示 root ticket A
  • react移动端适配

    一 自定义方法配置 1 使用JavaScript动态设置根元素的字体大小 为了实现响应式的自适应效果 可以结合JavaScript根据屏幕大小动态设置根元素的字体大小 可以使用window innerWidth获取窗口的宽度 然后根据需要的
  • Q_PROPERTY

    一 定义 Qt提供了一个成熟的属性系统 Q PROPERTY是一个宏 用来在类中声明一个属性 由于该特性是Qt所特有的 需要moc进行编译 故必须继承QObject 查看Qt助手 我们可以看到如下定义 看不懂 其实 这是一个正则表达式 我们
  • Selenium Web自动化测试框架实践

    功能实现 自动运行用例 自动生成测试报告 自动断言与截图 自动将最新测试报告发送到指定邮箱 自动生成测试报告 数据 页面元素 测试用例分离 执行日志 分布式执行 配置化文件 元素 数据 实践功能 https passport csdn ne