1. 项目概述:为什么我们需要对比测试框架?

干了这么多年自动化测试,从最早的QTP、Selenium IDE一路到现在,我最大的感受就是:工具选对了,活能轻松一半。最近带新人,发现他们面对RobotFramework、pytest和unittest这三个Python生态里的主流测试框架时,总是一脸懵,不知道从何下手。网上资料要么太散,要么就是“Hello World”级别的简单对比,看完还是不知道怎么选。

今天,我就以一个踩过无数坑的老测试的身份,来深度拆解这三个框架。这不仅仅是罗列特性,我会结合真实的项目场景——比如一个中等规模的Web应用、一个需要持续集成的API服务、一个维护了三年以上的老测试套件——来告诉你,在什么情况下该用谁,以及为什么。我的目标是,你看完这篇文章,不仅能知道每个框架的优缺点,更能建立起一套属于自己的“框架选型决策树”,下次面对新项目时,能快速、准确地做出最适合团队和业务的选择。

2. 三大框架核心定位与设计哲学剖析

要真正理解一个框架,不能只看它有什么功能,更要看它被设计出来是为了解决什么问题,它的“灵魂”是什么。这决定了它的上手成本、扩展方式和最终能发挥的威力上限。

2.1 RobotFramework:以“可读性”和“低代码”为核心的验收测试驱动框架

RobotFramework(后文简称RF)的诞生,源于一个非常明确的目标:让非技术背景的成员(如产品经理、业务分析师)也能参与甚至主导自动化测试用例的编写和审阅。它的核心设计哲学是“关键字驱动”和“行为驱动”的混合体。

为什么是表格语法? 这不是为了标新立异。表格(.robot文件)是一种对人类极其友好的结构化数据呈现方式。对于业务人员来说,看到“Open Browser”、“Input Text”、“Click Button”这样的纯英文句子,远比看到 driver.find_element(By.ID, “login”).click() 这样的代码要直观得多。RF通过将底层复杂的操作(可能是Selenium、Requests库的调用)封装成一个简单的“关键字”,实现了测试逻辑与实现细节的彻底分离。

它的优势场景非常聚焦

  1. 验收测试驱动开发 :在ATDD模式下,业务、开发和测试三方可以基于.robot文件这个“活文档”进行沟通,用例本身就是需求的可执行描述。
  2. 团队技能栈异构 :团队里有资深的自动化工程师,也有刚转行的功能测试人员。资深工程师负责用Python或Java编写底层的“库”和“关键字”,而功能测试人员则可以专注于用这些关键字组合成业务测试流。
  3. 报告驱动型项目 :RF内置生成的HTML报告和日志文件,其详细程度和美观度是三者中最好的。对于需要向管理层或客户直观展示测试覆盖率和结果的项目,这是一个巨大的加分项。

但它的代价也很明显

  • 灵活性受限 :当你需要处理复杂的数据结构(如嵌套的JSON)、实现精巧的控制流(如基于动态条件的循环)时,RF的表格语法会变得笨拙甚至难以实现。虽然可以通过“用户关键字”和Python代码块来增强,但这已经偏离了其“低代码”的初衷。
  • 执行效率 :RF的解析器和关键字匹配机制带来了一定的开销。在拥有数千条用例的大型套件中,其执行速度通常慢于纯pytest或unittest。
  • 调试体验 :当关键字执行失败时,你需要一层层点开HTML日志,追溯到最底层的Python库代码才能找到根本原因,这个过程对开发者不如IDE的断点调试直观。

注意 :不要试图用RF去写单元测试或需要复杂数据准备的集成测试。它是一把专门为验收和端到端业务流程测试打造的“瑞士军刀”,虽然什么都能干点,但最锋利的还是开罐头(业务流验证)的那部分。

2.2 pytest:以“简洁”和“强大”为核心的Python原生测试框架

如果说RF是为业务人员设计的,那么pytest就是为Python开发者量身定制的。它的设计哲学是“让写测试变得简单而有趣,让扩展测试变得强大而灵活”。它几乎重新定义了Python社区对测试的认知。

“约定大于配置”的极致体现 :你不需要继承任何类,不需要记住一堆固定的方法名。只要你的函数名以 test_ 开头,或者类名以 Test 开头且其中的方法以 test_ 开头,pytest就能自动发现并执行它们。这种极简的约定,极大地减少了样板代码。

它的强大,源于其可组合的“夹具”系统 @pytest.fixture 装饰器是pytest的灵魂。你可以用它来定义测试前置条件(如初始化数据库连接、创建临时文件)、测试数据,甚至是复杂的测试环境搭建。

import pytest

@pytest.fixture(scope="session")
def database_connection():
    # 模拟建立数据库连接,整个测试会话只执行一次
    conn = create_db_conn()
    yield conn  # yield之前是setup,之后是teardown
    conn.close()

@pytest.fixture
def clean_user(database_connection):
    # 每个测试函数执行前,清空用户表并插入一条测试数据
    database_connection.execute("DELETE FROM users")
    user_id = database_connection.execute("INSERT INTO users ...").lastrowid
    yield user_id
    # 如果需要,可以在这里做更精细的清理

def test_user_login(clean_user, database_connection):
    # 测试函数直接使用fixture返回的数据
    user_id = clean_user
    # 执行登录逻辑断言...
    assert login(user_id) is True

为什么fixture模式如此优秀? 因为它实现了依赖注入。测试函数声明它需要什么(通过参数),pytest负责提供。这使得:

  1. 代码复用性极高 :一套环境搭建代码可以被所有测试用例共享。
  2. 资源管理清晰 :通过 scope 参数(function, class, module, session)精确控制fixture的生命周期,避免重复初始化,优化执行速度。
  3. 测试隔离性好 :每个测试函数获得的fixture实例默认是独立的,减少了测试间的相互干扰。

此外,pytest的插件生态是其另一个护城河 pytest-html 生成报告, pytest-xdist 实现分布式并行测试, pytest-cov 集成覆盖率, pytest-mock 方便打桩。几乎你遇到的任何测试需求,都能找到对应的插件。这使得pytest不仅能做单元测试,更是接口自动化、UI自动化(结合Selenium)的绝佳底座。

2.3 unittest:Python标准库中的“古典派”

unittest是Python自带的标准库模块,它的设计深受Java的JUnit框架影响。它的核心是面向对象和继承:你的测试类必须继承 unittest.TestCase ,测试方法必须以 test 开头,断言使用 self.assertXxx() 系列方法。

它的最大优势是“无需额外依赖”和“结构严谨” 。因为是标准库,在任何Python环境中都可用,这对于环境受限(如某些封闭的部署环境)或项目规范要求极简依赖的情况,是唯一的选择。其基于类的结构,对于熟悉JUnit、NUnit等xUnit体系的人来说非常亲切,学习成本低。

然而,它的“古典”也带来了明显的时代局限性

  1. 样板代码多 :每个测试类都需要继承,setup/teardown方法有固定的命名( setUp , tearDown ),不够灵活。
  2. 断言信息不友好 self.assertEqual(a, b) 在失败时,默认输出的信息比较简陋,不如pytest的断言能直接展示差异值。
  3. 扩展性较弱 :虽然可以通过加载测试套件等方式进行组织,但缺少pytest那样丰富的插件生态和灵活的fixture机制,在管理复杂测试依赖和参数化时显得力不从心。
  4. 发现机制笨拙 :需要手动或通过 unittest.defaultTestLoader 来发现测试,不如pytest的自动发现智能。

那么unittest用在哪儿? 它非常适合小型工具库、脚本的单元测试,或者作为团队技术转型期的过渡方案。如果你的团队刚从Java转来Python,或者项目规模很小且稳定,不希望引入任何第三方依赖,unittest是一个可靠、不会出错的选择。

3. 核心能力维度对比与选型决策树

了解了各自的设计哲学,我们再把它们拉到同一个擂台上,从几个对项目成败至关重要的维度进行量化对比。我会用一个虚拟的“电商平台测试”需求来贯穿这些场景。

对比维度 RobotFramework pytest unittest 场景化解读
语法与可读性 极高 (表格,类自然语言) (纯Python,简洁) (基于类,样板代码多) 给产品经理演示用例 :RF的.robot文件完胜。 团队内部代码评审 :pytest的Python代码更受开发欢迎。
学习与上手成本 (需学RF语法和关键字) (Python开发者零门槛) (简单,但模式固定) 新人入职 :Python背景的同事,pytest半小时就能写出测试;RF需要几天熟悉关键字和资源文件概念。
灵活性与扩展性 (通过自定义库扩展,但语法是瓶颈) 极高 (原生Python+丰富插件) (标准库模式,扩展困难) 需要测试手机H5页面 :pytest可轻松集成Appium;RF需找或写AppiumLibrary;unittest集成起来较繁琐。
断言能力 (内置关键字,如 Should Be Equal 极高 (智能断言,直接使用 assert ,失败信息详细) self.assertXxx() 系列) 断言一个复杂API返回的JSON :pytest可以 assert resp.json()[‘user’][‘address’] == ‘xxx’ ,失败时直接打印出不匹配的值,调试效率极高。
测试数据驱动 内置支持强 [Template] , 外部数据文件) 极强且灵活 @pytest.mark.parametrize 需手动实现 (通过 subTest 或循环) 用10组用户数据测试登录功能 :pytest用 parametrize 一行装饰器搞定,用例报告里会清晰展示每组数据的结果。RF用 [Template] 也不错,但数据格式处理上稍弱。
测试夹具与依赖管理 (Suite/Test Setup/Teardown, 作用域固定) 极强 @pytest.fixture , 多级作用域,依赖注入) setUp/tearDown , setUpClass/tearDownClass 测试需要登录态 :pytest的 @pytest.fixture(scope=”module”) 可以让你只登录一次,供整个模块的测试用例使用,大幅提速。RF和unittest的setup在每个用例或套件级别,可能造成重复登录。
报告与日志 极佳 (详细、美观的HTML报告和日志,开箱即用) (需插件如 pytest-html , 可高度自定义) (文本报告,不直观) 每日构建报告发给项目经理 :RF的报告最省心。pytest搭配 pytest-html allure-pytest 可以做出更炫酷、信息量更大的报告,但需要额外配置。
执行速度与并行 (解析和执行开销大) (原生Python执行, 插件 pytest-xdist 支持并行) (标准库,速度尚可) 拥有3000条UI测试用例 :pytest + xdist可以在多核机器上并行跑,可能将1小时的执行时间缩短到10分钟。RF并行需要额外的工具(如Pabot),且效率提升不如pytest明显。
社区与生态 活跃 (有较多预置库) 极其活跃 (海量插件, 事实标准) 稳定 (标准库, 变化慢) 想集成钉钉通知测试结果 :pytest有现成插件;RF和unittest可能需要自己写脚本调用API。

基于以上对比,我们可以提炼出一个简单的 决策树 ,帮助你在项目启动时快速做出选择:

  1. 首要问题:测试用例的主要阅读和编写者是谁?

    • 如果是产品、业务或测试分析师 ,他们需要直接参与 -> 优先选择RobotFramework
    • 如果是开发人员或自动化测试工程师 -> 进入下一问题。
  2. 次要问题:项目对测试的灵活性、扩展性和执行性能要求高吗?

    • ,项目复杂,需要参数化、复杂夹具、定制化报告、并行执行等 -> 毫不犹豫选择pytest
    • ,项目简单、稳定,或是遗留系统,希望零依赖、快速启动 -> 可以考虑unittest
  3. 附加问题:是否有强烈的“活文档”或“业务可读”需求?

    • ,测试用例本身要作为需求文档 -> RobotFramework 优势明显。
    • ,更关注测试的技术实现和效率 -> pytest 更胜一筹。

一个经验法则 :对于全新的、中大型的、技术栈以Python为主的测试项目, pytest是当前社区最主流、最推荐的选择 。它平衡了能力、效率和优雅。RF在特定领域(强业务驱动、跨角色协作)不可替代。unittest则守住其“标准库”的底线,在微小、保守的场景中发挥作用。

4. 混合使用策略与迁移路径规划

在实际项目中,情况往往不是非此即彼。聪明的团队会根据不同测试层级和目的,混合使用这些框架,发挥各自长处。

4.1 典型混合架构:pytest + RobotFramework

这是我在多个成功项目中验证过的模式,特别适用于有完整测试金字塔的团队。

  • 金字塔底层(单元测试、集成测试) 使用pytest 。针对单个函数、类、模块间的接口进行测试。利用pytest的快速、灵活和强大断言,保证代码质量。这部分主要由开发人员编写和维护。
  • 金字塔中层(API/服务测试) 使用pytest 。结合 requests 库和 pytest 的参数化、夹具功能,高效验证后端API的准确性和性能。自动化测试工程师主导。
  • 金字塔顶层(UI端到端测试、验收测试) 使用RobotFramework 。覆盖核心的用户业务流程,如“用户从登录到下单支付”。用例由测试分析师和产品经理共同设计,使用业务友好的关键字编写。生成的精美报告直接用于迭代评审。

这种架构的好处

  1. 职责清晰 :技术性测试用技术框架(pytest),业务性测试用业务框架(RF)。
  2. 效率最大化 :底层快速反馈,顶层保障业务。RF执行慢的缺点被限制在少量核心流程测试中,不影响整体反馈速度。
  3. 协作顺畅 :不同角色使用最适合自己的工具。

4.2 从unittest或RF向pytest迁移

很多团队是从unittest起步,或者早期引入了RF。随着项目复杂度的提升,可能会考虑向更强大的pytest迁移。这里有一些平滑迁移的心得。

从unittest迁移到pytest是最容易的 ,因为pytest完全兼容unittest的测试用例。你可以直接运行现有的unittest测试套件,无需任何修改:

pytest your_old_unittest_file.py

迁移可以逐步进行。新写的测试直接用pytest风格,老的测试用例在有空时再逐步重构成使用fixture和参数化。pytest的 @pytest.mark.unittest 装饰器还可以帮你分类管理这些遗留用例。

从RobotFramework迁移到pytest则是一个“重写”的过程 ,因为两者范式不同。不建议一次性全部迁移,风险高、收益不确定。更务实的策略是:

  1. 冻结RF :停止在RF中编写新的复杂测试,尤其是那些需要大量逻辑判断和数据处理的。
  2. 新模块用pytest :所有新功能、新模块的自动化测试,直接使用pytest来编写。
  3. 核心流程逐步重构 :选择RF中最核心、但维护成本最高的1-2个业务流程,用pytest + Selenium/Playwright 等重写。对比两者的维护成本和稳定性。
  4. 评估与决策 :根据重构的效果,决定是继续迁移,还是维持RF+pytest的混合状态。很多时候,混合状态就是最终状态。

5. 实战配置与常见“坑点”实录

光说不练假把式。下面我以搭建一个Web自动化测试项目为例,分别展示用pytest和RobotFramework的初始化配置,并分享几个我踩过的“大坑”。

5.1 pytest + Selenium 快速搭建与最佳实践

项目结构 (这是我常用的,清晰且可扩展):

your_project/
├── conftest.py          # 全局夹具配置,如浏览器驱动
├── pytest.ini           # pytest配置文件
├── requirements.txt     # 依赖包列表
├── page_objects/        # 页面对象模型目录
│   ├── __init__.py
│   ├── login_page.py
│   └── home_page.py
├── tests/
│   ├── __init__.py
│   ├── conftest.py      # 测试目录特有的夹具
│   ├── test_login.py
│   └── test_search.py
└── utils/
    ├── __init__.py
    └── data_loader.py

核心 conftest.py 配置(浏览器夹具)

import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

@pytest.fixture(scope="session")
def browser_config():
    """全局配置,决定用什么浏览器、是否无头模式等"""
    config = {"headless": True, "browser": "chrome"}
    # 可以从环境变量或配置文件读取,实现环境差异化配置
    return config

@pytest.fixture(scope="function")  # 默认每个测试函数一个浏览器实例,保证隔离
def driver(browser_config):
    """最重要的夹具:提供WebDriver实例"""
    options = Options()
    if browser_config["headless"]:
        options.add_argument("--headless")
        options.add_argument("--disable-gpu")
        options.add_argument("--window-size=1920,1080")

    if browser_config["browser"] == "chrome":
        # 建议使用webdriver-manager自动管理驱动,避免版本问题
        from webdriver_manager.chrome import ChromeDriverManager
        from selenium.webdriver.chrome.service import Service
        service = Service(ChromeDriverManager().install())
        driver = webdriver.Chrome(service=service, options=options)
    elif browser_config["browser"] == "firefox":
        # ... 类似配置
        pass
    else:
        raise ValueError(f"Unsupported browser: {browser_config['browser']}")

    driver.implicitly_wait(10)  # 隐式等待,非必需,看团队习惯
    yield driver
    # 测试结束后,无论成功失败,都退出浏览器
    driver.quit()

一个使用了页面对象和参数化的测试示例 ( tests/test_login.py ):

import pytest
from page_objects.login_page import LoginPage

class TestLogin:
    """登录功能测试"""

    @pytest.mark.parametrize("username, password, expected", [
        ("valid_user", "valid_pass", True),
        ("invalid_user", "valid_pass", False),
        ("valid_user", "", False),  # 密码为空
        ("", "valid_pass", False),  # 用户名为空
    ])
    def test_login_with_different_inputs(self, driver, username, password, expected):
        """使用参数化测试多种登录场景"""
        login_page = LoginPage(driver)
        login_page.open()  # 打开登录页
        login_page.enter_username(username)
        login_page.enter_password(password)
        login_page.click_submit()

        if expected:
            # 期望登录成功,应跳转到首页
            assert "dashboard" in driver.current_url
            assert login_page.is_logout_button_displayed()
        else:
            # 期望登录失败,应停留在登录页并有错误提示
            assert "login" in driver.current_url
            assert login_page.is_error_message_displayed()

我踩过的坑与心得

  1. driver 夹具作用域选择 :我曾图省事,将 driver 夹具设为 scope="session" ,希望所有测试复用同一个浏览器实例来提速。结果就是测试用例间严重相互干扰,A用例留下的cookie或页面状态影响了B用例,导致诡异且难以复现的失败。 血的教训:UI自动化测试,除非有非常清晰的清理逻辑,否则 driver 夹具尽量用 scope="function" ,保证用例独立。提速应该靠 pytest-xdist 并行,而不是共享浏览器。
  2. 隐式等待与显式等待混用 driver.implicitly_wait(10) 是全局设置,它会让 find_element 在找不到元素时等待最多10秒。但如果和显式等待( WebDriverWait )混用,逻辑会变得复杂,等待时间可能叠加,导致脚本变慢。 我的建议:明确只用一种。对于现代Web应用,推荐使用 pytest wait 夹具(需额外封装)或直接使用显式等待,因为它条件更灵活(如等待元素可点击、可见),代码意图更清晰。
  3. 测试数据管理 :不要把测试数据硬编码在测试用例里!像上面的 @pytest.mark.parametrize ,当数据量多时,会让测试函数变得冗长。 最佳实践是将测试数据外置,如放在JSON、YAML或Excel文件中,在 conftest.py 中通过夹具加载。 例如:
    # conftest.py
    import json
    import pytest
    
    @pytest.fixture(scope="session")
    def login_test_data():
        with open("test_data/login_cases.json", "r") as f:
            return json.load(f)
    
    # test_login.py
    @pytest.mark.parametrize("case", login_test_data())
    def test_login(self, driver, case):
        username = case["username"]
        # ... 使用数据
    

5.2 RobotFramework 关键配置与避坑指南

基础项目结构

rf_project/
├── resources/
│   ├── common_keywords.robot   # 公共自定义关键字
│   └── variables.robot         # 全局变量
├── tests/
│   ├── __init__.robot
│   ├── smoke_tests/
│   │   └── login_smoke.robot
│   └── regression_tests/
│       └── checkout_flow.robot
├── libraries/                  # 自定义Python库
│   └── MyCustomLibrary.py
└── outputs/                    # 报告输出目录(.gitignore忽略)

一个典型的 .robot 文件示例 ( tests/smoke_tests/login_smoke.robot ):

*** Settings ***
Documentation    登录功能冒烟测试
Library          SeleniumLibrary
Resource         ../resources/common_keywords.robot
Suite Setup      Open Browser To Login Page
Suite Teardown   Close All Browsers
Test Setup       Go To Login Page
Test Template    Login With Credentials Should Fail

*** Variables ***
${VALID_USER}    demo_user
${VALID_PASS}    demo_pass
${LOGIN_URL}     https://example.com/login

*** Test Cases ***               USERNAME            PASSWORD        ERROR_MESSAGE
Invalid Username                 invalid_user        ${VALID_PASS}   用户名或密码错误
Invalid Password                 ${VALID_USER}       wrong_pass      用户名或密码错误
Empty Username                   ${EMPTY}            ${VALID_PASS}   请输入用户名
Empty Password                   ${VALID_USER}       ${EMPTY}        请输入密码

Valid Login
    [Documentation]    有效登录应成功
    [Tags]    smoke    high
    Input Text         id=username    ${VALID_USER}
    Input Password     id=password    ${VALID_PASS}
    Click Button       id=login-btn
    Location Should Be    https://example.com/dashboard
    Page Should Contain    欢迎回来,${VALID_USER}

*** Keywords ***
Open Browser To Login Page
    Open Browser    ${LOGIN_URL}    chrome
    Maximize Browser Window
    Set Selenium Speed    0.5 seconds    # 稍微放慢速度,便于观察和稳定

Go To Login Page
    Go To    ${LOGIN_URL}
    Wait Until Page Contains Element    id=username    timeout=10s

Login With Credentials Should Fail
    [Arguments]    ${username}    ${password}    ${expected_error}
    Go To Login Page
    Input Text      id=username    ${username}
    Input Password  id=password    ${password}
    Click Button    id=login-btn
    Wait Until Page Contains    ${expected_error}    timeout=5s
    Page Should Contain    ${expected_error}

RF实战中的“坑”与技巧

  1. 变量作用域混乱 :RF的变量作用域(Global, Suite, Test, Local)是新手最容易迷糊的地方。在 *** Variables *** 部分定义的是全局或套件变量。在关键字中用 Set Test Variable 设置的变量只在当前测试用例有效。 技巧:尽量使用 ${} 语法在关键字间传递参数,减少对全局变量的依赖。对于配置项(如URL、浏览器),统一放在资源文件 variables.robot 中管理。
  2. 等待策略导致的不稳定 :RF的SeleniumLibrary提供了 Wait Until ... 关键字,但很多新手喜欢用 Sleep ,这是不稳定测试的根源。 必须使用显式等待 。同时, Set Selenium Speed 可以全局设置每个操作后的延迟,在调试时有用,但在正式执行时应设为0或一个很小的值。
  3. 自定义库的导入路径问题 :当你写了自己的Python库( MyCustomLibrary.py ),放在 libraries 目录下,在 *** Settings *** 中直接用 Library MyCustomLibrary 可能会导入失败。 解决方案
    • libraries 目录添加到Python的 PYTHONPATH 环境变量中。
    • 或者在执行RF时通过 --pythonpath 参数指定路径: robot --pythonpath libraries/ tests/
    • 更规范的做法是将自定义库打包成标准的Python包(带 setup.py pyproject.toml ),然后用 pip install -e . 安装到当前环境。
  4. 测试数据与代码分离 :上面的例子中,测试数据是写在 Test Cases 表格里的。对于更复杂的数据, 强烈推荐使用外部文件 ,如CSV、Excel或JSON。RF可以通过 Library Collections OperatingSystem 库来读取这些文件,然后在测试模板中循环使用。这能让你的.robot文件更清爽,更专注于业务逻辑。

6. 框架选型之外的思考:什么比工具更重要?

最后,我想跳出工具对比,谈点更重要的东西。我见过太多团队在框架选型上争论不休,却忽略了自动化测试成功更本质的要素。

1. 团队技能与共识是基石 。选择一个团队大部分成员都能愉快使用并愿意维护的框架,远比选择一个“最强大”的框架重要。如果团队都是Python高手,却强行上马RF,结果就是开发人员抵触,写出的自定义库质量差,最终项目烂尾。 技术决策本质上是人的决策。

2. 维护成本是长期关键 。自动化测试不是一锤子买卖。今天用最酷的技术搭起来的框架,如果结构混乱、缺乏文档、用例脆弱(充满了 Sleep 和绝对定位),三个月后就会变成没人敢动的“遗产代码”。无论选择哪个框架,都要从一开始就关注: * 页面对象模型 :将页面元素和操作封装起来,业务测试用例只调用高层关键字。 * 测试数据外部化 :数据和逻辑分离。 * 清晰的目录结构和命名规范 。 * 持续集成 :测试要能自动触发、快速反馈。

3. 适合的才是最好的 。没有银弹。一个主要做API测试的微服务团队,pytest + requests + pytest-html 的组合可能完美。一个需要业务和测试紧密协作的金融项目,RF带来的“活文档”价值可能远超其执行效率的损失。一个维护历史悠久、测试用例全是unittest的遗留系统,渐进式地引入pytest比推倒重来更明智。

在我个人看来, pytest凭借其优雅的设计、强大的功能和活跃的生态,已经成为Python自动化测试领域的事实标准 。对于大多数从零开始的新项目,它是我首推的起点。但这绝不意味着RF或unittest没有价值。理解它们的差异,看清项目的真实需求和团队的实际情况,你才能做出那个“不会在半年后让自己后悔”的技术选型。

更多推荐