Skip to main content
Tip

Short answer. Choose seleniumbase-rs if your tests or tooling already live in Rust, if you want to ship the runner as one native binary, or if you want the pure CDP engine and the agent tools that upstream Python SeleniumBase does not have. Do not choose it because you expect faster tests: the browser sets the pace (see Performance).

You are…Then
A Rust team testing a Rust serviceUse it: share types and run tests with cargo test
Shipping a test runner to CI or to people who do not write RustUse it: sbrs is one binary
Building AI-agent tooling around a browserUse it: the MCP servers explain failures and inspect pages
A Python team with a large SeleniumBase suiteStay on Python unless you have another reason
Wanting speed from the same testsMeasure first; expect little change

This page explains when the crate is the right choice and when another stack may be a better fit. It does not claim Rust is universally better.

What this crate is#

seleniumbase-rs is a Rust port of the Python SeleniumBase framework (it tracks 4.55.2) and of its MCP server. It keeps the productive BaseCase API and anti-detection features, but runs on Rust’s async runtime, type system, and native distribution model. It has two engines: a WebDriver engine built on thirtyfour, and a pure Chrome DevTools Protocol engine (sb_cdp) that needs no driver at all. Every API of the upstream project has a recorded status in the parity table: 888 implemented, 383 composed from existing Rust calls, 94 not applicable (for example pytest plumbing) and none still planned. That table is a claim, not a measurement; two audits have corrected it, and most of it is checked against a mock or unit tests rather than a real browser.

When Rust helps most#

Choose seleniumbase-rs when the test harness itself benefits from strong compile-time checks, predictable resource ownership, native binaries, or integration with an existing Rust workspace.

Refactoring safety#

Browser tests are full of strings—URLs, selectors, and expected text. Rust cannot catch a broken selector, but it can catch a renamed helper, a changed assertion signature, or a mistyped method call. This turns a large class of “works on my machine but fails in CI” bugs into compiler errors that you fix before pushing.

For example, if you rename a helper from login_as to sign_in_as, every broken call site is reported at compile time:

The browser still validates the page, but the harness no longer hides trivial structural mistakes.

Controlled concurrency#

Rust’s ownership model and Tokio make shared state explicit. This is valuable when tests need to coordinate with each other, share connection pools, or run in a controlled number of parallel browser processes. The run_browser_test helper also makes cleanup deterministic: it awaits the test body and then quits the WebDriver session, reporting cleanup failures instead of dropping them.

Native distribution#

The sbrs CLI compiles to a single native binary. You do not need to manage a Python virtual environment, resolve pip conflicts, or ship a Node runtime. This is especially useful in CI/CD images, on restricted machines, or when tests are run by operators who are not Rust developers.

Shared codebase#

If your services are already written in Rust, tests can reuse the same structs, serializers, validation logic, and client code. Keeping test data models in sync with production code becomes a compile-time guarantee instead of a manual task.

Feature flags and dependency control#

Optional features such as tui, gui, playwright, s3, azure, gcp, mcp-server, turso, full-tracing, ocr and plugins are compiled in only when you enable them. The default feature set is empty, so default builds stay small, and you can audit exactly which dependencies are included for each target (cargo deny check and cargo audit run against the lockfile).

What the Rust port adds beyond Python#

These exist in this crate and not in upstream Python SeleniumBase. The pure CDP engine, the input engine, the pool, interception and the identity application were checked against a real Chrome 155; the agent tools have unit tests and three real-Chrome tests (diagnose_environment ran only against a mock):

  • A pure CDP engine with no driver. Browser, Page and Locator talk to Chrome directly, so there is no chromedriver version to match. A mock browser lets tests run without Chrome.
  • A behavioural input engine. Curved pointer paths and human typing pace (Page::human).
  • An async browser pool. BrowserPool and Lease hand out isolated, recycled browser contexts with shared session state.
  • Request interception and per-context proxies. Block, fulfil or modify requests per page, give each context its own proxy (including password protected ones), and shield WebRTC leaks.
  • Coherent randomised identities. Fingerprint::randomized(os, seed) draws a whole machine and place. The tables are plausible, not measured from real traffic, and nothing has been run against a live bot-detection service.
  • Tools for AI agents that develop and debug tests. The MCP servers answer a failed call with the page state, a hint and the next tools to try, and offer page inspection, selector diagnosis, a console and failed-request view, an accessibility audit, a debug bundle, an environment check and an export of the session as a Rust test.
  • Errors for people. Every error has a hint, sbrs exits with a status that follows the error’s category, and failed tests carry their page and evidence into the reports.
Note

Not here: TLS (JA3/JA4) and HTTP/2 (Akamai) impersonation, the impersonating HTTP session, the MITM proxy and the combined stealth session moved in 0.3.0 to the separate, experimental seleniumbase-rs-stealth project, measured for one browser and system (Chrome 155 on macOS). See Stealth Scraping.

Evidence from this crate#

These are things the type system and the toolchain did here, not general claims about Rust:

  • Secrets cannot leak by formatting. A Proxy with credentials never shows its password in Debug, Display or the command line, and the scraping types (RedactedUrl, the egress key) redact on construction and are never serialised with the secret. Tests assert this.
  • A new case breaks the build, not the run. The challenge detector’s ChallengeKind and the mitigation policy’s Action are enums matched exhaustively; adding a variant makes every match that ignores it fail to compile.
  • Bad option combinations fail before launch. LaunchOptions::builder() .build() rejects incognito together with guest, or use_chromium with an explicit executable_path, before Chrome starts.
  • No unsafe. The crate has no unsafe blocks, so memory-safety bugs would have to come from a dependency.
  • Testing without a browser. Browser::new_mocked swaps the Chrome connection for a scripted one behind the same types, so most of the pure CDP engine is tested without launching Chrome. The default run has 1,600 passing tests; the real-Chrome ones are #[ignore]d and run one binary at a time.
  • Dependencies are auditable. cargo deny check and cargo audit run against the lockfile (currently no vulnerabilities, 3 allowed unmaintained warnings).
Warning

Rust did not prevent every bug. During this port the WebDriver path sent a proxy login as an extra header to every site, which is a logic error the compiler cannot see; a review found it and a test now guards it. Timing-dependent tests also flaked until they were made to wait for what they check.

What it costs#

  • Compile time and memory. A full check, lint and test run takes many minutes on an 8 GB machine, which is why the project gates once per batch.
  • A learning curve. Test authors need to be comfortable with Rust, async and the borrow checker.
  • A smaller ecosystem. Python SeleniumBase has far more examples and plugins; Python plugins cannot be reused here.
  • Unfinished parts. The parity table’s composed and not-applicable entries are still to be independently reviewed, and the stealth scraping layer (now a separate project) is young and measured for one browser and system only.

What does not change#

Info

Rust does not make the browser faster. Page loads, JavaScript execution, network round trips, and browser startup still dominate most end-to-end tests. A Rust test that opens a heavy web page waits just as long as a Python or JavaScript test opening the same page.

Rust also does not fix flaky selectors, race conditions in the application under test, or unstable infrastructure. Those problems require the same test-design discipline in every language.

Practical comparison#

AreaRust advantageImportant limit
RefactoringCompiler catches renamed methods and signature changes before CISelectors and page behavior still fail at runtime
ConcurrencyTokio tasks and explicit shared state support controlled parallelismBrowsers remain expensive external processes
Cleanuprun_browser_test makes session teardown deterministicAsync cleanup cannot run from Drop
DistributionSingle native binary with no Python or Node runtimeBrowser and driver installation are still required
DependenciesCargo lockfiles and feature flags provide reproducible selectionDependencies still require auditing and updates
IntegrationTests can share typed models with Rust servicesCross-language teams may prefer their existing stack

Compared to other stacks#

Python SeleniumBase has the largest existing example library and the most mature upstream ecosystem. It is often the right choice when:

  • Your team already writes Python.
  • You rely on SeleniumBase Python plugins or a large existing test suite.
  • You need the fastest path to a working test.

seleniumbase-rs is worth considering when you want:

  • Compile-time guarantees across a growing test suite.
  • A single native binary for CI or distribution.
  • To share code with Rust services.
  • Fine-grained control over dependencies via feature flags.

How to decide#

Use this checklist:

  • Is the test suite large enough that refactoring mistakes are costly?
  • Is the harness maintained by Rust developers or integrated with Rust services?
  • Is native distribution or CI simplicity important?
  • Do you need anti-detection features that are available in this crate?
  • Are you willing to invest in porting or writing tests in Rust?

If most answers are yes, seleniumbase-rs is likely a good fit. If your team is happy with Python or JavaScript and does not share code with Rust, staying in that ecosystem may be more productive.