自动化测试

一.概念

⾃动化的主要⽬的就是⽤来进行回归测试

即软件有多个版本需要进行功能的整体回归 。 为了避免新增功能影响到历史的功能需要进⾏功能的回归。

一定程度上。测试工作不仅仅包含测试执行阶段。

测试主要由人工进行,自动化测试知识辅助。

1.自动化


然后实际上在企业中,⾃动化往往是"冰淇淋蛋筒反模式"

  • 越往底层(单元 / API)投入自动化,投入产出比越高
  • 越往上层(UI / 手动)投入,维护成本和时间成本越高,效率越低。
  • 理想的测试策略是:单元测试 + API 自动化为主,UI 自动化为辅,手动测试聚焦探索性场景
  • 把自动化重心放在单元测试和 API 测试上,它们的维护成本低、执行效率高,能快速提供反馈。
  • UI 自动化仅用于核心业务流程的回归,不要过度覆盖,避免维护成本失控。
  • 手动测试专注于探索性测试、用户体验和边缘场景,不要把时间浪费在重复的回归用例上。

避免蛋筒模式的核心,就是让自动化测试 "下沉":单元测试打底,接口测试为主,UI 自动化为辅,手动测试做补充。这样既能保证产品质量,又能让自动化投入获得最高回报。

2.web自动化测试

程序想要打开web浏览器就需要安装web驱动(WebDriver),WebDriver以本地化⽅式驱动浏览器

项目底部控制台

3.selenium

Selenium 是网页自动化工具,用来代码操控浏览器,模拟人手动上网操作

编写自动化脚本需要selenium工具

当出现这些 代表配置好了


ctrl+Shift+i进入开发者模式

然后按小箭头 变蓝后可以指哪就得到其id

python 复制代码
#编写自动化脚本
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

# 1.打开浏览器 驱动-driver
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
time.sleep(2)
# 2.输入百度网址http://www.sogou.com(一定要完整)
driver.get("http://www.sogou.com")
time.sleep(2)
# 3.找到输入框 输入关键词"杨幂"
driver.execute_script("document.getElementById('query').value='杨幂'")
# 4.点击"百度一下"按钮进行查找
driver.execute_script("document.getElementById('stb').click()")
time.sleep(2)
# 5.关闭浏览器
driver.quit()
复制代码
ChromeDriverManager().install()
  • driver.execute_script :执行 JS 代码(最稳定,永远不报错)
  • document:整个网页
  • getElementById('query') :找到 id 为 query 的搜索框
  • value=' 杨幂' :给输入框赋值 = 杨幂
  • 每个 Chrome 版本,必须有一个对应的小驱动程序,否则代码控制不了浏览器。可以管理:

selenium+驱动+浏览器的⼯作原理

二.常用函数

1.查找


对于同类型:

  • > 叫「直接子元素选择器」
  • 作用:只选父元素的 "亲儿子",不选 "孙子" 或更后面的后代
  • 大白话:
    • #hotsearch-content-wrapper > li → 只找这个 div 下面第一层直接的 li
    • 如果 li 被包在别的标签里(比如 div > ul > li),用 > 就找不到了

符号 含义 大白话
/ 只找下一层的直接子节点 必须是 "亲儿子",不能跳层
// 找所有层级的后代节点 不管中间隔了多少层,只要在里面就行


定位方式 亲儿子(直接相邻) 所有后代(可以跨层)
CSS 父元素 > 子元素 父元素 子元素(中间一个空格)
XPath 父节点/子节点 父节点//子节点

2.操作



写法 清除用什么 可以用 clear () 吗?
Selenium 标准 .clear() ✅ 可以
JavaScript .value = ''(置空) ❌ 不可以

断言 = 代码里的 "检查点" 作用:判断某个条件是否成立,不成立就直接报错,终止程序

元素属性值!=文本信息



3.窗口

点击图片 获取新的网页的title和url

每个浏览器窗口都有唯一的属性句柄(handle) 通过handle来切换

(逻辑:遍历所有标签页,找到和原来句柄不一样的那个(也就是新打开的标签),然后用 driver.switch_to.window()driver 的视角切换过去)



strftime:格式化时间


4.弹窗

提示弹窗:

5.等待



(由于方法的参数只有一个 所以需要用括号将By...和'...'组合成一个元组)

注意:

6.浏览器导航


7.文件上传

8.浏览器参数设置


三.实战

1.编写测试用例

2.参考测试用例 编写自动化测试脚本

Utils:

python 复制代码
# 创建一个浏览器对象
import datetime
import os
import sys

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

class Driver:
    # 相当于给这个工具预留一个放浏览器的位置
    driver = ""
    # 构造方法:只要这个工具一被创建,就自动执行这里面的代码
    def __init__(self):
        options = webdriver.ChromeOptions()
        self.driver=webdriver.Chrome(service=Service(ChromeDriverManager().install()),
                                   options=options)
    # 创建屏幕截图
    def getScreenshot(self):
        # 图片文件名称:./2026-05-25-173456.png
        # 图片路径:../images/调用方法-2026-05-25/2026-05-25-173456.png
        dirname=datetime.datetime.now().strftime("%Y-%m-%d")
        # 判断dirname文件夹是否存在 不存在则创建
        if not os.path.exists("../images/"+dirname):
            os.makedirs("../images/"+dirname)
        filename=(sys._getframe().f_back.f_code.co_name+"-"
                  +datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S")+".png")
        self.driver.save_screenshot("../images/"+dirname+"/"+filename)


BlogDriver = Driver()

登录页面:

python 复制代码
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from common1.Utils import BlogDriver
from selenium.webdriver.support import expected_conditions as EC
# 测试博客登录页面

class BlogLogin:
    url=""
    driver=""
    def __init__(self):
        self.url="http://47.108.157.13:8090/blog_login.html"
        self.driver=BlogDriver.driver
        self.driver.get(self.url)

    # 成功登录的测试用例
    def LoginSucTest(self):
        self.driver.implicitly_wait(5)
        self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("zhangsan")
        self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")
        self.driver.find_element(By.CSS_SELECTOR,"#submit").click()
        # 能够找到博客首页的昵称 说明登陆成功
        success_elem=self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.left > div > h3")
        BlogDriver.getScreenshot()
        assert success_elem.is_displayed(),"登录失败 未找到首页元素"
        print("✅ 博客登录成功测试用例执行通过")
        self.driver.back()

    # 失败登录的测试用例
    def LoginFail_UserError(self):
        self.driver.implicitly_wait(5)
        self.driver.find_element(By.CSS_SELECTOR,"#username").clear()
        self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("zhangsann")
        self.driver.find_element(By.CSS_SELECTOR,"#password").clear()
        self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")
        self.driver.find_element(By.CSS_SELECTOR,"#submit").click()
        try:
            # 关键修复:等待弹窗出现!!!隐式等待等不了弹窗
            WebDriverWait(self.driver, 3).until(EC.alert_is_present())
            # 切换到弹窗
            alert = self.driver.switch_to.alert
            # 获取弹窗文字
            msg = alert.text
            print("弹窗提示:", msg)
            # 关闭弹窗
            alert.accept()
            # 关闭alert后才能截屏!!! 但此时也就没必要
            # BlogDriver.getScreenshot()
            # 能走到这里,说明弹窗出现了 → 测试正确
            print("✅ 用户名错误登录失败弹窗正常出现,测试通过!")
        except:
            # 没有弹窗 → 测试失败
            print("❌ 未出现弹窗,测试失败!")
            assert False

    def LoginFail_PassError(self):
        self.driver.implicitly_wait(5)
        self.driver.find_element(By.CSS_SELECTOR,"#username").clear()
        self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("zhangsan")
        self.driver.find_element(By.CSS_SELECTOR,"#password").clear()
        self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123")
        self.driver.find_element(By.CSS_SELECTOR,"#submit").click()
        try:
            WebDriverWait(self.driver, 3).until(EC.alert_is_present())
            alert = self.driver.switch_to.alert
            msg = alert.text
            print("弹窗提示"+msg)
            alert.accept()
            print("✅ 密码错误登录失败弹窗正常出现,测试通过!")
        except:
            print("❌ 未出现弹窗,测试失败!")
            assert False

    def LoginFail_UAPError(self):
        self.driver.implicitly_wait(5)
        self.driver.find_element(By.CSS_SELECTOR,"#username").clear()
        self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("zhangggsan")
        self.driver.find_element(By.CSS_SELECTOR,"#password").clear()
        self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123")
        self.driver.find_element(By.CSS_SELECTOR,"#submit").click()
        try:
            WebDriverWait(self.driver, 3).until(EC.alert_is_present())
            alert = self.driver.switch_to.alert
            msg = alert.text
            print("弹窗提示"+msg)
            alert.accept()
            print("✅ 用户名 + 密码错误登录失败弹窗正常出现,测试通过!")
        except:
            print("❌ 未出现弹窗,测试失败!")
            assert False

大量重复代码 可优化:

python 复制代码
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from common1.Utils import BlogDriver
from selenium.webdriver.support import expected_conditions as EC

# 测试博客登录页面
class BlogLogin:
    url = ""
    driver = ""

    def __init__(self):
        self.url = "http://47.108.157.13:8090/blog_login.html"
        self.driver = BlogDriver.driver
        self.driver.get(self.url)

    # ==========================
    # 【提取公共方法】输入账号密码 + 清空 + 登录
    # ==========================
    def input_login(self, username, password):
        self.driver.implicitly_wait(5)
        self.driver.find_element(By.CSS_SELECTOR, "#username").clear()
        self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys(username)
        self.driver.find_element(By.CSS_SELECTOR, "#password").clear()
        self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys(password)
        self.driver.find_element(By.CSS_SELECTOR, "#submit").click()

    # ==========================
    # 【提取公共方法】处理弹窗
    # ==========================
    def check_alert(self, success_msg):
        try:
            WebDriverWait(self.driver, 3).until(EC.alert_is_present())
            alert = self.driver.switch_to.alert
            msg = alert.text
            print("弹窗提示:" + msg)
            alert.accept()
            print(f"✅ {success_msg}")
        except:
            print("❌ 未出现弹窗,测试失败!")
            assert False

    # ==========================
    # 测试用例(现在超级干净)
    # ==========================

    # 成功登录的测试用例
    def LoginSucTest(self):
        self.input_login("zhangsan", "123456")
        success_elem = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.left > div > h3")
        BlogDriver.getScreenshot()
        assert success_elem.is_displayed(), "登录失败 未找到首页元素"
        print("✅ 博客登录成功测试用例执行通过")
        self.driver.back()

    # 失败登录:用户名错误
    def LoginFail_UserError(self):
        self.input_login("zhangsann", "123456")
        self.check_alert("用户名错误登录失败弹窗正常出现,测试通过!")

    # 失败登录:密码错误
    def LoginFail_PassError(self):
        self.input_login("zhangsan", "123")
        self.check_alert("密码错误登录失败弹窗正常出现,测试通过!")

    # 失败登录:用户名+密码错误
    def LoginFail_UAPError(self):
        self.input_login("zhangggsan", "123")
        self.check_alert("用户名 + 密码错误登录失败弹窗正常出现,测试通过!")


# 执行用例
login = BlogLogin()
login.LoginSucTest()
login.LoginFail_UserError()
login.LoginFail_PassError()
login.LoginFail_UAPError()
login.driver.quit()

当有了main之后 其他类下不能有执行代码


博客首页界面:

Python 程序的入口点,作用是:

  1. 按顺序执行多个测试用例

  2. 只要把执行代码写在 runTest.pyif __name__ == "__main__" 里面:

    不管你在哪个文件里右键点 "运行",只有 runTest.py 里的代码会执行

    执行顺序完全按照写的顺序来 ,其他文件绝对不会自己乱运行

python 复制代码
# 博客登录首页
from selenium.common import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common1.Utils import BlogDriver

class BlogList:
    def __init__(self):
        url=""
        driver=""
        self.url="http://47.108.157.13:8090/blog_list.html"
        self.driver=BlogDriver.driver

    def ListSucTest(self):
        self.driver.implicitly_wait(3)
        # 测试昵称是否存在
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.left > div > h3")
        # 测试头像是否存在
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.left > div > img")
        # 测试博客标题是否存在
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div:nth-child(2) > div.title")
        # 测试博客正文是否存在
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div:nth-child(2) > div.desc")
        # 测试"查看全文:按钮是否存在
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div:nth-child(2) > a")
        # 添加屏幕截图
        BlogDriver.getScreenshot()
        print("登录成功 主页展示完成")

    def ListFailTest(self):
        # 进入页面
        self.driver.get(self.url)
        # 等待弹窗出现(未登录拦截的证据!)
        try:
            WebDriverWait(self.driver, 3).until(EC.alert_is_present())
            alert = self.driver.switch_to.alert
            assert "用户未登录" in alert.text
            print("✅ 未登录访问列表页:弹窗已出现,用例通过!")
            alert.accept()
        except:
            print("❌ 未登录用例失败")
            assert False

我们要先验证未登录直接进入的情况 此时RunTest的执行顺序调整为:

python 复制代码
from common1.Utils import BlogDriver
from tests1.BlogList import BlogList
from tests1.BlogLogin import BlogLogin

if __name__ == "__main__":
    # 1. 先执行:未登录访问列表页(失败用例)
    print("=== 未登录访问列表页 ===")
    BlogList().ListFailTest()
    # 2. 执行:登录失败用例
    print("=== 登录失败用例 ===")
    BlogLogin().LoginFail_UserError()
    BlogLogin().LoginFail_PassError()
    BlogLogin().LoginFail_UAPError()
    # 3. 执行:登录成功 → 列表页成功
    print("=== 登录成功 + 列表页成功 ===")
    BlogLogin().LoginSucTest()
    BlogList().ListSucTest()

    # 关闭浏览器
    print("=== 所有用例执行完毕 ===")
    BlogDriver.driver.quit()

博客详情页:

python 复制代码
# 博客详情页
from selenium.webdriver.common.by import By
from common1.Utils import BlogDriver


class BlogDetail:
    url = ""
    driver = ""
    def __init__(self):
        self.url="http://47.108.157.13:8090/blog_detail.html?blogId=1"
        self.driver = BlogDriver.driver

    # 登录状态下博客详情页的测试
    def DetailSucTest(self):
        self.driver.get(self.url)
        # 有发布时间
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div > div.date")
        # 有发布标题
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div > div.title")
        # 有删除按钮
        self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div > div.operating > button:nth-child(2)")
        # 添加屏幕截图
        BlogDriver.getScreenshot()
        print("详情页展示完成")

博客编辑页面:

python 复制代码
# 博客编辑页面
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from common1.Utils import BlogDriver

class BlogEdit:
    url=""
    driver=""
    def __init__(self):
        self.url="http://47.108.157.13:8090/blog_edit.html"
        self.driver=BlogDriver.driver
        self.driver.get(self.url)

    # 正常发布博客(标题 内容 发布)
    def EditSucTest(self):
        self.driver.implicitly_wait(3)
        self.driver.find_element(By.CSS_SELECTOR, "#title").send_keys("今天是5.26")
        # 找到编辑区域,输入关键词(编辑区域不可操作)
        # 菜单栏无法元素无法定位
        # 本博客系统编辑区域默认情况下就不为空,可以暂不处理
        self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
        try:
            WebDriverWait(self.driver, 3).until(EC.alert_is_present())
            alert = self.driver.switch_to.alert
            print("成功弹窗提示:", alert.text)
            alert.accept() 
        except:
            pass
        BlogDriver.getScreenshot()
        print("发布博客成功")

    # 异常情况(不写标题)
    def EditFail_title(self):
        self.driver.implicitly_wait(3)
        self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
        try:
            WebDriverWait(self.driver, 3).until(EC.alert_is_present())
            alert=self.driver.switch_to.alert
            assert "参数校验失败" in alert.text
            print("发布博客失败(参数校验失败)")
            alert.accept()
        except:
            assert False, "未弹出校验弹窗"

最后 所有代码的隐式等待可以统一写到Utils:



类函数里 self 到底有什么用

Python self = Java this,只是 Python 要求必须显性写出来

特性 Python self Java this
含义 当前实例对象 当前实例对象
是否要手动声明 必须写第一个参数 不用写,隐含
能否改名 可以 不可以,关键字
访问成员 必须写 self. 可省略
能否传参返回 可以 不可以

四.测试报告

=======================================================================

博客系统测试报告

=======================================================================

相关推荐
测试开发-学习笔记5 分钟前
代码详细解释
python
u01196082315 分钟前
ray-k8s部署
python
PAK向日葵3 小时前
我用 C++ 写了一个轻量级 Python 虚拟机,刚刚开源
c++·python·开源
财经资讯数据_灵砚智能4 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年5月26日
大数据·人工智能·python·信息可视化·自然语言处理·ai编程·灵砚智能
我材不敲代码5 小时前
Python基础:列表详解、增删改查及常用高阶操作
开发语言·windows·python
AI玫瑰助手5 小时前
Python运算符:成员运算符(in/not in)的使用场景
开发语言·python·信息可视化
Warson_L5 小时前
python - class 入门
python
水木流年追梦5 小时前
大模型入门-大模型分布式训练2
开发语言·分布式·python·算法·正则表达式·prompt
ZHANG8023ZHEN6 小时前
Diffusion 数学推理
人工智能·python·机器学习
海天一色y6 小时前
SGLang 本地部署 Qwen3-8B 大模型实战指南
python·sglang