GEO

为什么我要构建Smartest测试运行器?Playwright风格fixture注入实战

2026/5/4
为什么我要构建Smartest测试运行器?Playwright风格fixture注入实战

AIAI Summary (BLUF)

This article explores why the author built Smartest, a new Ruby test runner inspired by Playwright Test, focusing on its pytest-style fixture injection with explicit dependencies and cleanup, aiming to improve browser testing in Rails.

原文翻译:本文探讨了作者为何构建Smartest(一个受Playwright Test启发的Ruby测试运行器),重点介绍其具有显式依赖和清理的pytest风格fixture注入,旨在改进Rails浏览器测试。

Why I Built Another Ruby Test Runner Inspired by Playwright Test

Ruby already has great testing tools.

Ruby 已经拥有了优秀的测试工具。

If you are building Rails applications today, you probably use one of these combinations:

如果你现在正在构建 Rails 应用,你很可能使用以下组合之一:

  • RSpec + Capybara
  • Minitest + Capybara
  • Rails system tests
  • Maybe Selenium, Cuprite, Ferrum, or Playwright through Ruby bindings

These tools are mature, battle-tested, and widely used.

这些工具已经成熟、久经考验且被广泛使用。

So the natural question is:

所以自然的问题是:

Why create yet another test runner?

为什么要再创造一个测试运行器?

That is exactly the question the Playwright team asked themselves when they created Playwright Test.

这正是 Playwright 团队在创建 Playwright Test 时问自己的问题。

In the Playwright video that inspired this project, the speaker says they did not originally want to create another JavaScript testing framework. They tried existing frameworks such as Mocha and Jest, but those frameworks had historically been designed for unit testing. End-to-end testing had a different set of problems: cross-browser execution, parallelization, browser-side isolation, and a high degree of fixture flexibility.

在启发这个项目的 Playwright 视频中,演讲者表示他们原本并不想再创建一个 JavaScript 测试框架。他们尝试过 Mocha 和 Jest 等现有框架,但那些框架从历史上看都是为单元测试设计的。端到端测试面临一系列不同的问题:跨浏览器执行、并行化、浏览器端隔离以及高度灵活的 fixture 机制。

That explanation clicked with me.

这个解释让我茅塞顿开。

Because Ruby on Rails browser testing has a similar problem.

因为 Ruby on Rails 的浏览器测试也有类似的问题。

RSpec and Minitest are excellent general-purpose Ruby test frameworks. Capybara is an excellent browser automation DSL. But the current mainstream Rails browser testing style was not designed after learning from Playwright Test. It still feels like a unit-test-era architecture with browser automation added on top.

RSpec 和 Minitest 是优秀的通用 Ruby 测试框架。Capybara 是出色的浏览器自动化 DSL。但当前主流的 Rails 浏览器测试风格并不是在借鉴 Playwright Test 之后设计的。它仍然感觉像是单元测试时代的架构,然后在上面叠加了浏览器自动化。

Playwright Test showed that browser testing deserves a test runner designed around browser testing itself.

Playwright Test 表明,浏览器测试值得拥有一个围绕浏览器测试本身设计的测试运行器。

That is the idea behind Smartest.

这就是 Smartest 背后的理念。


The Inspiration: Playwright Test Was Not Just Another Runner

Playwright Test is not only a wrapper around browser automation.

Playwright Test 不仅仅是浏览器自动化的一个封装。

It is a test runner designed for the real constraints of end-to-end testing:

它是一个为端到端测试的真实约束而设计的测试运行器:

  • Cross-browser support out of the box
  • Parallel execution
  • Browser-context isolation
  • Web-first assertions
  • Traceability and debugging support
  • Fixtures inspired by pytest

The important point is not simply “Playwright controls browsers.”

重要的不仅仅是“Playwright 控制浏览器”。

The important point is:

重要的点是:

Playwright Test changed the test runner because the target domain changed.

Playwright Test 改变了测试运行器,因为目标领域发生了变化。

Browser tests are not just unit tests with a browser.

浏览器测试不仅仅是加了浏览器的单元测试。

They need their own setup model.

它们需要自己的设置模型。


The Fixture Idea I Wanted in Ruby

One of the best ideas in pytest is fixture injection.

pytest 中最好的想法之一就是 fixture 注入。

In pytest, a test can request what it needs by naming it:

在 pytest 中,测试可以通过名称来请求它所需的东西:

def test_get_me(logged_in_client):
    response = logged_in_client.get("/me")
    assert response.status_code == 200

The test body does not say how to create logged_in_client.

测试主体并没有说明如何创建 logged_in_client

The fixture system knows how to resolve it.

fixture 系统知道如何解析它。

Fixtures can depend on other fixtures:

Fixture 可以依赖其他 fixture:

@pytest.fixture
def logged_in_client(client, user):
    client.login(user)
    return client

This is extremely useful for browser tests, because browser tests often have layered setup:

这对于浏览器测试非常有用,因为浏览器测试通常涉及分层设置:

Layer Description
App server Application under test
Browser runtime Browser process
Browser context Isolated session
Page Single page instance
User Test user data
Login state Authenticated session
Seeded data Pre-populated database
Cleanup Teardown after test

RSpec and Minitest can express all of this, of course.

当然,RSpec 和 Minitest 也能实现所有这些。

But the dependency graph is often implicit.

但依赖关系图往往是隐式的。

With RSpec, we may write something like this:

使用 RSpec,我们可能会写出类似这样的代码:

let(:server) { start_server }
let(:client) { Client.new(base_url: server.url) }
let(:user) { create_user }
let(:logged_in_client) do
  client.login(user)
  client
end

it "GET /me" do
  response = logged_in_client.get("/me")
  expect(response.status).to eq(200)
end

This works.

这能工作。

But the fixture dependency graph is hidden inside method calls.

但 fixture 的依赖关系图隐藏在方法调用中。

I wanted this instead:

而我希望的是这样的:

test("GET /me") do |logged_in_client:|
  response = logged_in_client.get("/me")

  expect(response.status).to eq(200)
end

And fixture definitions like this:

以及这样的 fixture 定义:

class AppFixture < Smartest::Fixture
  fixture :client do |server:|
    Client.new(base_url: server.url)
  end

  fixture :logged_in_client do |client:, user:|
    client.login(user)
    client
  end
end

The important design decision is the keyword argument.

重要的设计决策是关键字参数。

test("GET /me") do |logged_in_client:|

This makes the test's dependencies explicit.

这使测试的依赖关系变得明确。

And:

并且:

fixture :logged_in_client do |client:, user:|

This makes the fixture's dependencies explicit.

这使 fixture 的依赖关系变得明确。

No positional argument order.
No hidden let dependency.
No separate with: [:server] declaration.

没有位置参数的顺序问题。
没有隐式的 let 依赖。
没有单独的 with: [:server] 声明。

The dependency declaration and usage live in one place: the Ruby method signature.

依赖的声明和使用融合在一个地方:Ruby 的方法签名。


Smartest: A Small Keyword-Fixture-First Ruby Test Runner

Smartest is a small Ruby test runner with a keyword-fixture-first design.

Smartest 是一个小型 Ruby 测试运行器,采用关键字 fixture 优先的设计。

The goal is not to replace all Ruby testing immediately.

目标不是立即取代所有 Ruby 测试。

The goal is to explore what Ruby testing could feel like if we applied the lessons of pytest fixtures and Playwright Test to Ruby browser testing.

目标是探索如果将 pytest fixture 和 Playwright Test 的经验应用到 Ruby 浏览器测试中,Ruby 测试会是什么感觉。

A normal Smartest test looks like this:

一个普通的 Smartest 测试看起来像这样:

test("factorial") do
  expect(1 * 2 * 3).to eq(6)
end

A fixture-driven test looks like this:

一个由 fixture 驱动的测试看起来像这样:

test("GET /me") do |logged_in_client:|
  response = logged_in_client.get("/me")
  expect(response.status).to eq(200)
end

Smartest is designed around three ideas:

Smartest 围绕三个理念设计:

  1. Tests should be readable at the top level.

    测试应在顶层可读。

  2. Fixture dependencies should be explicit.

    Fixture 依赖应显式声明。

  3. Teardown should be written only when it is...

    清理代码应仅在需要时编写……

(Note: The original article continues, but this rewrite focuses on the introduction, key concepts, and main analysis. For a complete guide, please refer to the official documentation.)


Summary: A Comparison of Smartest with Traditional Ruby Test Frameworks

Feature RSpec / Minitest (with Capybara) Smartest
Fixture injection Implicit via let or before blocks Explicit via keyword arguments in test method signature
Dependency graph Hidden (resolved at runtime via scope) Visible in the method signature of both tests and fixtures
Fixture hierarchy Manual setup via nested describe or before Automatic dependency resolution between fixtures
Parallel execution Possible with extra gems (e.g., parallel_tests) Designed with Playwright-inspired parallelism in mind
Cleanup after blocks or let! with instance variables Built-in cleanup block inside fixture definitions
Browser-specific features Via Capybara (auto-wait, driver abstraction) Direct integration with Playwright (via Ruby bindings)
Learning curve Low (familiar to Ruby devs) Low to medium (new keyword-fixture paradigm)

Smartest does not aim to replace RSpec or Minitest for every use case. It aims to provide a better experience for browser testing by adopting ideas that have proven successful in the JavaScript (Playwright) and Python (pytest) ecosystems.

Smartest 并非旨在取代所有场景下的 RSpec 或 Minitest。它旨在通过采用在 JavaScript(Playwright)和 Python(pytest)生态系统中已被证明成功的理念,为 浏览器测试 提供更好的体验。


For the complete project repository and documentation, visit:

常见问题(FAQ)

为什么已经有了RSpec和Minitest,还要构建新的Ruby测试运行器?

因为现有框架为单元测试设计,而浏览器测试需要跨浏览器执行、并行化和灵活fixture等特性,Smartest专为浏览器测试设计。

Smartest的fixture注入和传统的Ruby测试框架有什么不同?

Smartest采用pytest风格fixture注入,测试通过名称显式依赖并自动解析,支持fixture间依赖和清理,比RSpec的let/let!更清晰。

Smartest如何受Playwright Test启发?

Playwright Test证明浏览器测试需要专门的测试运行器,Smartest借鉴其设计理念,包括面向端到端测试的fixture模型和更好的隔离性。

← 返回文章列表
分享到:微博

版权与免责声明:本文仅用于信息分享与交流,不构成任何形式的法律、投资、医疗或其他专业建议,也不构成对任何结果的承诺或保证。

文中提及的商标、品牌、Logo、产品名称及相关图片/素材,其权利归各自合法权利人所有。本站内容可能基于公开资料整理,亦可能使用 AI 辅助生成或润色;我们尽力确保准确与合规,但不保证完整性、时效性与适用性,请读者自行甄别并以官方信息为准。

若本文内容或素材涉嫌侵权、隐私不当或存在错误,请相关权利人/当事人联系本站,我们将及时核实并采取删除、修正或下架等处理措施。 也请勿在评论或联系信息中提交身份证号、手机号、住址等个人敏感信息。