Selenium + PyTest — 高效集成实践指南
本文最后更新于 187 天前,其中的信息可能已经有所发展或是发生改变。

碎碎念

自从软件交付节奏不断加快,测试从“事后补救”走向“开发驱动”,测试框架的选型直接决定了项目的维护效率与工程质量。在自动化测试领域,Selenium 凭借其强大的浏览器驱动能力早已成为行业标准,而 PyTest 以简洁灵活的语法、强大的插件生态迅速崛起,成为 Python 测试框架的首选。将两者结合,能显著提升测试代码的组织性与可维护性,实现测试逻辑与浏览器操作的高效协同。

本文将以 12306 网站为测试对象,深入讲解如何基于 Selenium 与 pytest 构建一套结构清晰、可扩展、适配持续集成的自动化测试体系。从项目结构设计到用例管理,从参数化配置到断言优化,我们将以实际代码为载体,探索自动化测试在复杂 Web 场景下的最佳实践路径。

需要提醒的是,本文假设你已经学习并实践了 Selenium WebDriver — Web自动化测试标准工具 – Ethan的博客 ,该文章中有对任务背景及 Selenium 用法的必要介绍。你可以在 Ethan的云盘中获取本文的完整实践代码,Good Luck!


1. Pytest 概述

1.1 Pytest 历史

pytest的历史始于2002年PyPy项目,其团队在开发过程中特别重视测试框架的改进。最初他们使用Python标准库的unittest.py,但发现其过于繁琐。2004年,核心开发者Holger Krekel在Europython大会上提出了”std”项目(后改名为py库),其中包含了一个名为std.utest的子模块,这就是pytest的前身。这个模块创新性地实现了”魔法断言”功能,能够自动解析断言表达式并输出详细的错误信息,大幅提升了测试的便利性。2004年9月,py库正式从PyPy项目中分离出来独立发展。经过多年的完善,2010年发布的pytest 2.0.0标志着它成为一个完全独立的测试框架。2016年的3.0.0版本又将命令行入口从py.test简化为pytest,使其更加易用。如今,pytest凭借其简洁的语法和强大的插件系统,已成为Python生态系统中最受欢迎的测试框架,而其”零样板代码”的设计理念从诞生之初就一直延续至今。

1.2 Pytest 安装

你可以通过 pip 安装 pytest 包:

pip <span class="hljs-keyword">install</span> pytest

当然,比起直接用 pip 安装,我更推荐使用 Anaconda 来管理环境和依赖。你可以在左边栏 Environments 里方便的搜索到 pytest 包 :

QQ截图20250601102909.jpg

在终端键入以下命令,如出现版本号则说明安装成功:

pytest <span class="hljs-comment">--version</span>

2. Pytest 基本用法

在继续 12306 测试的第四步前,了解一些最基本的 Pytest 知识是必要的。

2.1 Pytest 默认测试用例

pytest 默认测试用例的格式:

  • 模块名:模块通常被统一放在一个 testcases 文件夹中,需要保证模块名以 test_ 开头或 _test 结尾,例如 test_demo1.pydemo2_test.py
  • 类名:测试类类名必须以 Test 开头,并且不能带有 init 方法
  • 方法名:测试方法名(Case 名)必须以test_开头,例如test_demo1(self)test_demo2(self)

QQ截图20250601103727.jpg

2.2 全局配置文件 pytest.ini

我们可以在 pytest.ini 中进行一些属性的配置来指定 Pytest 按何种方式执行测试,在此之前需要在根目录下创建,名称必须是 pytest.ini。以下是常见的属性配置:

[pytest]
<span class="hljs-meta"># 执行pytest命令的附加参数</span>
addopts = ‐vs
<span class="hljs-meta"># 默认的执行路径,它会默认执行该文件夹下所有的满足条件的测试case</span>
testpaths = ./testcases    
<span class="hljs-meta"># 文件命名规则</span>
python_files = test_*.py
<span class="hljs-meta"># 类名命名规则</span>
python_classes = Test*
<span class="hljs-meta"># Case命名规则</span>
python_functions = test_*

<span class="hljs-meta"># 标记</span>
markers =        
<span class="hljs-meta"># 冒烟规则</span>
smoke:冒烟用例         
product_manage:商品管理

是不是有点像 docker-compose. yaml 或 build. gradle 之类的配置文件 ?

2.3 执行 pytest

最简单的执行方式就是直接在终端命令行输入 pytest,如果存在 pytest.ini,它会根据文件内容执行测试;如果没有就按照默认格式执行测试。我们可以通过一些参数来强化 pytest 参数指令:

<span class="hljs-meta"># -vs: -v输出详细信息 -s输出调试信息</span>
pytest -vs

<span class="hljs-meta"># -n: 多线程运行(前提安装插件:pytest-xdist)</span>
pytest -vs -n=<span class="hljs-number">2</span>

<span class="hljs-meta"># --reruns num: 失败重跑(前提安装插件:pytest-rerunfailres)</span>
pytest -vs --reruns=<span class="hljs-number">2</span>

<span class="hljs-meta"># -x: 出现一个用例失败则停止测试</span>
pytest -vs -x

<span class="hljs-meta"># --maxfail: 出现几个失败才终止</span>
pytest -vs --maxfail=<span class="hljs-number">2</span>

<span class="hljs-meta"># --html: 生成html的测试报告,后面 需要跟上所创建的文件位置及文件名称(前提安装插件:pytest-html)</span>
pytest -vs --html ./reports/result.html

<span class="hljs-meta"># -k: 运行测试用例名称中包含某个字符串的测试用例,我们可以采用or表示或者,采用and表示都</span>
pytest -vs -k <span class="hljs-string">"qiuluo"</span>
pytest -vs -k <span class="hljs-string">"qiuluo or weiliang"</span>
pytest -vs -k <span class="hljs-string">"qiuluo and weiliang"</span>

<span class="hljs-meta"># -m:一组用例执行,后面需要跟一个组名称,执行user_manage这个分组</span>
pytest -vs -m user_manage

除了在终端直接键入命令外,你也可以通过 main 方法来执行:

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    pytest.main()

<span class="hljs-meta"># 带参数的main方法</span>
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    pytest.main([<span class="hljs-string">"‐vs"</span>])

2.4 对测试进行分组

Pytest 允许我们在测试函数上使用标记。标记用于设置各种特征/属性来测试功能。Pytest 提供了许多内置标记,例如 xfail、skip 和 parametrize。除此之外,用户可以创建自己的标记名称。使用下面给出的语法将标记应用于测试:

@<span class="hljs-keyword">pytest</span>.<span class="hljs-keyword">mark</span>.<<span class="hljs-keyword">markername</span>>

给出代码示例:

<span class="hljs-comment"># test_demo1.py</span>
<span class="hljs-comment"># author: ethan</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestDemo</span>:</span>
    <span class="hljs-comment"># 我们在Case上采用<span class="hljs-doctag">@pytest</span>.mark. + 分组名称,就相当于该方法被划分为该分组中</span>
    <span class="hljs-comment"># 注意:一个分组可以有多个方法(case),一个方法也可以被划分到多个分组中</span>
    @pytest.mark.user_manage
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_demo1</span>
<span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"user_manage_test1"</span>)

    @pytest.mark.product_manage
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_demo2</span>
<span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"product_manage_test1"</span>)

    @pytest.mark.user_manage
    @pytest.mark.product_manage
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_demo3</span>
<span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"manage_test1"</span>)

尝试执行一下:

<span class="hljs-comment"># 确保你已经按照本文方法配置了 pytest.int</span>
<span class="hljs-attribute">pytest</span> -vs -m user_manage

测试结果如下,成功执行了分组为 user_manage 的两条测试用例!

5.jpg

2.5 跳过某些不需要的 case

Pytest 的跳过方法其实和 unittest 是完全相同的,我们只需采用 skipskipif 方法来指定参数并贴在方法上即可跳过,给出代码示例:

<span class="hljs-comment"># <span class="hljs-doctag">@pytest</span>.mark.skip(跳过原因)</span>

<span class="hljs-comment"># <span class="hljs-doctag">@pytest</span>.mark.skipif(跳过条件,跳过原因)</span>

<span class="hljs-comment"># 示例</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestDemo</span>:</span>

    workage2 = <span class="hljs-number">5</span>
    workage3 = <span class="hljs-number">20</span>

    @pytest.mark.skip(reason=<span class="hljs-string">"无理由跳过"</span>)
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_demo1</span>
<span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"我被跳过了"</span>)

    @pytest.mark.skipif(workage2<<span class="hljs-number">10</span>,reason=<span class="hljs-string">"工作经验少于10年跳过"</span>)    
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_demo2</span>
<span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"由于经验不足,我被跳过了"</span>)

    @pytest.mark.skipif(workage3<<span class="hljs-number">10</span>,reason=<span class="hljs-string">"工作经验少于10年跳过"</span>)
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_demo3</span>
<span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"由于经验过关,我被执行了"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_demo3</span>
<span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
        print(<span class="hljs-string">"我没有跳过条件,所以我被执行了"</span>)

2.6 断言

Pytest 使用 assert 表达式进行断言,基本语法如下:

<span class="hljs-attribute">assert</span>
<span class="hljs-meta"> [expression]</span>

<span class="hljs-comment"># 带 description 的 assert 表达式。</span>
<span class="hljs-comment"># 如果断言失败,description 作为 AssertionError的内容展示</span>
<span class="hljs-attribute">assert</span>
<span class="hljs-meta"> [expression,description]</span>

给出示例代码如下:

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_case_01</span>
<span class="hljs-params">()</span>:</span> 
    print(<span class="hljs-string">"进入测试函数"</span>) 
    <span class="hljs-keyword">assert</span> add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">5</span> 
    print(<span class="hljs-string">"断言成功"</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_case_03</span>
<span class="hljs-params">()</span>:</span> 
    print(<span class="hljs-string">"进入测试函数"</span>) 
    <span class="hljs-comment"># 带描述性错误消息的用法 </span>
    <span class="hljs-keyword">assert</span> add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">10</span>, <span class="hljs-string">"结果与预期不符"</span> 
    print(<span class="hljs-string">"断言失败"</span>) <span class="hljs-comment"># 不会被执行</span>

至此,我们掌握了 pytest 的基本用法,更高级的用法(夹具、参数化测试等)可以到 Pytest教程中学习。接下来我们完成 12306 网站测试任务的第四步。

3. Selenium + Pytest

有了基础知识的铺垫,完成第四步就非常简单了。第一步,我们需要把上一篇文章中实现的返回历时最短车次的逻辑封装成一个函数:

<span class="hljs-meta">#-----------------------------------------  </span>
<span class="hljs-meta"># get_shortest_duration_train  </span>
<span class="hljs-meta"># 输入 :无  </span>
<span class="hljs-meta"># 输出 :字典(历时最短车辆信息)  </span>
<span class="hljs-meta"># 描述 :  </span>
<span class="hljs-meta">#-----------------------------------------  </span>

def get_shortest_duration_train():
    # .... 具体逻辑
    <span class="hljs-keyword">return</span> shortest_train

然后编写测试用例:

<span class="hljs-comment">#-------------------------------------  </span>
<span class="hljs-comment"># test_shortestTrain.py  </span>
<span class="hljs-comment"># Author : 周正  </span>
<span class="hljs-comment"># Email :ethan.zheng.zhou@outlook.com  </span>
<span class="hljs-comment"># Description :  </span>
<span class="hljs-comment">#  测试用例  </span>
<span class="hljs-comment">#--------------------------------------  </span>

<span class="hljs-keyword">import</span> sys  
<span class="hljs-keyword">import</span> os  
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), <span class="hljs-string">"../../.."</span>)))  

<span class="hljs-keyword">from</span> SeleniumPyTest <span class="hljs-keyword">import</span> get_shortest_duration_train  

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestDemo</span>:</span>  

<span class="hljs-meta">    @classmethod  </span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">setup_class</span>
<span class="hljs-params">(cls)</span>:</span>  
        <span class="hljs-string">"""在所有测试前执行一次,获取最短车次信息"""</span>  
        cls.shortest_train = get_shortest_duration_train()  

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_number</span>
<span class="hljs-params">(self)</span>:</span>  
        <span class="hljs-keyword">assert</span> self.shortest_train[<span class="hljs-string">'number'</span>] == <span class="hljs-string">'G9'</span>  

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_startTime</span>
<span class="hljs-params">(self)</span>:</span>  
        <span class="hljs-keyword">assert</span> self.shortest_train[<span class="hljs-string">'start_time'</span>] == <span class="hljs-string">'14:34'</span>  

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_endTime</span>
<span class="hljs-params">(self)</span>:</span>  
        <span class="hljs-keyword">assert</span> self.shortest_train[<span class="hljs-string">'end_time'</span>] == <span class="hljs-string">'15:37'</span>  

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_duration</span>
<span class="hljs-params">(self)</span>:</span>  
        <span class="hljs-keyword">assert</span> self.shortest_train[<span class="hljs-string">'duration'</span>] == <span class="hljs-string">'01:03'</span>

键入命令并查看测试结果:

<span class="hljs-attribute">pytest</span>

17.jpg

至此,12306 网站测试任务成功完成!

写给看到最后的你

感谢你坚持阅读完这篇Selenium与Pytest的整合指南!通过12306 测试案例的完整实现,相信你已经掌握了从基础断言到复杂测试框架搭建的核心方法论。自动化测试的真正价值在于将质量保障转化为可复用的工程资产,而持续探索才是应对快速迭代的技术生态的不二法门。如果在实践过程中遇到任何挑战,欢迎在评论区评论或通过邮件与我探讨。

参考文章

Pytest – 教程 – 菜鸟教程 pytest 核心功能-调用pytest_w3cschool 完整的Pytest文档 — pytest documentation Selenium WebDriver — Web自动化测试标准工具 – Ethan的博客

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇