1. Home
  2. Blog
  3. Test Automation

Test Automation

Selenium vs Playwright: Key Differences, Code Compared and Which to Choose

Selenium has two decades of history and a huge ecosystem. Playwright is newer and includes more out of the box. Compare them on what matters day to day, read one test in both, and decide with confidence.

9 min read
Selenium vs Playwright: key differences, code compared and which to choose

Key takeaways

  • Selenium is a mature browser automation library built on the W3C WebDriver standard.
  • Playwright is a newer framework with auto-waiting, tracing and a test runner included.
  • For a new web project, Playwright is usually quicker to get productive with.
  • Selenium remains a sound choice for large existing suites, wider language needs and testing in real vendor browsers.

If you are starting a test automation project, the Selenium vs Playwright question will come up quickly. Search results can make it sound like a settled matter, but the right answer depends on your team, your existing tests and the browsers you must support.

This guide compares them on the things you will actually feel in daily work. The code samples show the typical style of each tool, and we did not run them for this article. We also avoid quoting speed percentages, because published benchmarks vary widely and often come from vendors. Measure on your own application if speed will decide the matter.

The short answer

Choose Playwright for most new web testing projects, particularly if your team can work in JavaScript, TypeScript, Python, Java or .NET. You get more features out of the box, and less time goes on infrastructure and flaky waits.

Choose Selenium if you already have a large, working suite, if you need a language Playwright does not officially support such as Ruby, or if you must test on the real browser builds and platforms available through a Selenium grid or device cloud.

Selenium vs Playwright side by side

Aspect Selenium Playwright
First released 2004 2020, by Microsoft
What it is Browser automation library Framework, with a runner in Node.js
Languages Java, Python, C#, Ruby, JavaScript TypeScript, JavaScript, Python, Java, .NET
Browsers Chrome, Firefox, Edge, Safari Chromium, Firefox, WebKit
Waiting Explicit waits you write Auto-waiting built in
Test runner Bring your own, like pytest or JUnit Included for Node.js, pytest plugin for Python
Parallel runs Selenium Grid or the test framework Built into its runner
Debugging Framework logs, screenshots you add Trace viewer, UI mode, codegen
Network mocking Limited, often extra tooling Built in
Community Very large and long established Large and growing quickly

Architecture: how each one talks to the browser

Selenium sends commands using the W3C WebDriver protocol, a formal standard. Your code talks to a driver, such as ChromeDriver, which controls the browser, with each command usually being a separate HTTP request. Because browser vendors support this standard, it has broad compatibility. A newer protocol, WebDriver BiDi, is adding two-way communication and event streaming to the standard.

Playwright keeps a persistent connection to the browser and uses its own patched builds of Chromium, Firefox and WebKit. Because the connection is direct and two-way, it can react to browser events, and each test can run in its own lightweight browser context. This is why most comparisons rate Playwright as faster, though the actual gap depends on your application and your tests.

One test in both tools

Here is the same login test, written in Python, against a public practice site.

Selenium

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def test_login(driver):
    driver.get("https://the-internet.herokuapp.com/login")
    driver.find_element(By.ID, "username").send_keys("tomsmith")
    driver.find_element(By.ID, "password").send_keys("SuperSecretPassword!")
    driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

    message = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.ID, "flash"))
    )
    assert "You logged into a secure area" in message.text

You also need a fixture to create and close the driver, as we show in our Selenium Python tutorial.

Playwright

from playwright.sync_api import Page, expect

def test_login(page: Page):
    page.goto("https://the-internet.herokuapp.com/login")
    page.get_by_label("Username").fill("tomsmith")
    page.get_by_label("Password").fill("SuperSecretPassword!")
    page.get_by_role("button", name="Login").click()

    expect(page.locator("#flash")).to_contain_text("You logged into a secure area")

This relies on the pytest-playwright plugin, which supplies the page fixture. It is shorter, mainly because the wait is built into expect, and because locators like get_by_role describe the page the way a user sees it. With Selenium, you can reduce this boilerplate considerably with helpers and the Page Object Model, which we recommend in either tool.

Waiting and flaky tests

Timing is the top cause of flaky browser tests, and it is where the two tools differ most in everyday use.

In Selenium, you are responsible for synchronisation. Explicit waits work well, but somebody has to remember them everywhere, and teams that forget often fall back on time.sleep. Our guide to Selenium waits explains how to do this correctly.

In Playwright, actions automatically check that an element is visible, stable, enabled and able to receive events before acting. Web-first assertions keep retrying until a timeout. You still can write flaky tests, for example with poor test data, but a major source of problems is handled by default.

Browsers and languages

This is where Selenium keeps a real advantage. Playwright’s WebKit build is close to Safari’s engine, but it is not the same thing as Safari on a Mac or iPhone. If real Safari behaviour is critical, Selenium with SafariDriver, or a real device cloud, gets you closer to what your users actually run.

Similarly, Playwright tests its bundled browser versions, though it can also drive installed Chrome and Edge channels. Selenium drives the browsers you install, which helps if you must support specific versions. For native mobile apps, neither is the full answer: Appium, which is built on WebDriver concepts, is the usual tool.

On languages, Selenium officially supports Ruby and has a very long history in Java teams. Playwright’s Node.js version also has the most complete tooling, since the full test runner is there. Other languages get the core library and integrate with their own test frameworks.

Debugging

Playwright has a clear lead here. Its trace viewer records each action, along with DOM snapshots, network calls and console logs, so you can step through a failed CI run after it has finished. Its UI mode and codegen help you write tests faster as well. See our Playwright tutorial for beginners for how to use them.

With Selenium, you assemble this yourself, from screenshots on failure, browser logs, video from your grid provider and reports from your test framework. It works well, and many teams have excellent setups, but it takes effort to build.

Network and API features

Playwright can intercept and mock network requests with a few lines of code, which is useful for testing error states:

page.route("**/api/orders", lambda route: route.fulfill(status=500))

It also includes an API request client, so you can create test data through an API before driving the browser. Selenium 4 has added some network features through the Chrome DevTools Protocol and BiDi, but support and convenience vary by browser and language. Many Selenium users add a proxy or a separate HTTP library, as in API testing tools.

Tip: Whichever tool you choose, keep browser tests focused on critical journeys, and push details down to lower levels. A smaller suite matters more than a faster tool. See our end-to-end testing best practices.

Parallel runs and scale

Playwright’s runner handles parallel workers and sharding across CI machines with simple options. Browser contexts are cheap, so isolation does not require a whole new browser for every test.

Selenium uses Selenium Grid to distribute tests across machines and browser versions, and test frameworks handle the parallelism. Grid is more to run, but it is proven at very large scale, and many commercial device clouds support it directly.

Should you migrate from Selenium to Playwright?

Not automatically. A working suite is valuable, and a rewrite takes time away from testing the product. Ask these questions first:

  1. Is there a measurable problem? Flaky tests or slow debugging are good reasons. “It is newer” is not.
  2. How big is the suite? A hundred tests can be moved. Ten thousand is a major project.
  3. Are your page objects clean? Good abstractions make the move far cheaper.
  4. Do you rely on Selenium-specific features? Check your grid and device needs first.

If you do move, go gradually. Write new tests in Playwright, migrate the flakiest old tests first, and run both side by side until the old suite is small enough to retire. Do not translate tests line by line, since that carries over sleeps and old locators. Avoid the mistakes covered in test automation mistakes as well.

How to choose

Your situation Reasonable choice
New web app, modern team Playwright
Large stable Selenium suite Stay with Selenium, and improve it
Team writes Ruby Selenium
Real Safari coverage is critical Selenium, or a device cloud
Flaky tests and hard CI debugging Try Playwright on a pilot
Native mobile apps Appium

Best of all, run a short pilot. Automate your three most awkward journeys in each tool against your own application, in your own CI, and compare the code, the speed and how easily you can debug a failure. One week of evidence beats any comparison article, including this one. If Cypress is also on your shortlist, read Playwright vs Cypress.

Final thoughts

Playwright is usually the easier place to start, and Selenium remains a dependable standard with a broad reach. Neither will save a poorly designed test suite. Choose based on your real constraints, invest in stable locators and good test data, and keep your suite focused on what matters most to your users.

Frequently asked questions

Is Playwright better than Selenium?

For many new web projects, yes, thanks to auto-waiting and built-in tooling. Selenium can be the better fit for existing suites, wider language needs or specific real browsers.

Is Playwright faster than Selenium?

It is commonly reported as faster because of its persistent browser connection and lightweight contexts. The real difference depends on your app, so measure it on your own tests.

Is Selenium outdated?

No. Selenium is actively maintained and built on a W3C standard, and it is still widely used. Selenium 4 and WebDriver BiDi continue to modernize it.

Can Playwright test real Safari?

Playwright tests with its own WebKit build, which is close to Safari’s engine but not identical. For real Safari or iOS behaviour, use real devices or SafariDriver.

Should a beginner learn Selenium or Playwright?

Many beginners find Playwright easier to start with. Selenium still appears in many job listings, so check your local market. The core concepts carry over between them.

Keep learning

Work with Stack Quora

Need help with testing, WordPress or SEO?

We help teams ship better software and grow their sites. Tell us what you are working on and we will suggest a clear next step.

Keep reading

More guides on testing, AI & modern development

Enjoyed this article? Pick a topic and jump straight into our newest tutorials.

Artificial Intelligence

LLM Structured Output Explained: Getting Reliable JSON From AI Models

LLM structured output explained with validation we ran in Pydantic: JSON mode and schemas, constrained decoding, the ways model output…

Read article →
DevOps

SSH Keys Explained: How They Work, ssh-keygen, Ed25519 and Safe Habits

SSH keys explained with keys we generated: public vs private keys, ssh-keygen, Ed25519 vs RSA, fingerprints, passphrases, ssh-agent, authorized_keys, config…

Read article →
DevOps

HTTP Caching Explained: Cache-Control, ETag and 304 Responses, With a Live Test

HTTP caching explained with a small server we ran: Cache-Control directives, max-age, no-cache vs no-store, ETag revalidation and 304 responses,…

Read article →
Test Automation

API Contract Testing Explained: Catch Breaking Changes Before They Ship

API contract testing explained with a schema check we ran: what a contract is, consumer-driven contracts, Pact, OpenAPI, which changes…

Read article →
Software Testing

Risk-Based Testing Explained: Score Risks, Build a Matrix and Test What Matters

Risk-based testing explained step by step: identifying risks, scoring likelihood and impact, building a risk matrix, mapping risk to test…

Read article →
Databases & SQL

SQL Constraints Explained: NOT NULL, UNIQUE, CHECK and FOREIGN KEY, Tested

SQL constraints explained with tests we ran: NOT NULL, UNIQUE, CHECK, DEFAULT, PRIMARY and FOREIGN KEY, the NULL loopholes, SQLite's…

Read article →
Databases & SQL

SQL Date Functions Explained: Adding, Comparing and Grouping Dates Without Bugs

SQL date functions explained with queries we ran: adding intervals, month-end surprises, date differences, the BETWEEN trap, grouping by day,…

Read article →
Software Development

Python asyncio Explained: async, await, gather and TaskGroup With Real Timings

Python asyncio explained with code we timed: coroutines, await, gather, TaskGroup, error handling, timeouts, limiting concurrency and the blocking-call mistake…

Read article →
Software Development

Debounce vs Throttle in JavaScript: How They Work, When to Use Each, Tested

Debounce vs throttle in JavaScript explained with timings we measured in Node.js: how each works, simple implementations, leading and trailing…

Read article →
Software Development

Python Context Managers Explained: The with Statement, contextlib and Real Uses

Python context managers explained with code we ran: how with works, __enter__ and __exit__, contextlib.contextmanager, suppress, ExitStack, database transactions and…

Read article →
Artificial Intelligence

LLM Temperature and Top-p Explained: How AI Models Choose Their Next Word

LLM temperature and top-p explained with a simulation we ran: how probabilities are reshaped, what top-p keeps, recommended settings by…

Read article →
DevOps

YAML Explained: Syntax, the Norway Problem and Other Traps We Tested

YAML explained with a parser test: basic syntax, lists and maps, the Norway problem, versions turning into floats, octal zip…

Read article →
DevOps

Cron Jobs Explained: Syntax, Real Schedules and the Mistakes That Break Them

Cron jobs explained with schedules we verified: the five fields, operators, real examples, the day-of-month OR day-of-week trap, environment problems,…

Read article →
Software Testing

Bug Life Cycle in Software Testing: Every Status, Who Owns It and Common Mistakes

The bug life cycle explained: every defect status from New to Closed, who owns each step, rejected, deferred and reopened…

Read article →
Test Automation

Property-Based Testing in Python: Hypothesis Finds the Bugs Your Examples Miss

Property-based testing explained with Hypothesis tests we ran: properties vs examples, strategies, shrinking, the real bugs it found in our…

Read article →
Databases & SQL

SQL Pagination Explained: OFFSET vs Keyset, With a Million-Row Test

SQL pagination explained with a million-row test we ran: LIMIT and OFFSET, why deep pages get slow, keyset (cursor) pagination,…

Read article →
Databases & SQL

SQL EXISTS vs IN: Differences, the NOT IN NULL Trap and Performance

SQL EXISTS vs IN explained with queries we ran: how each works, why NOT IN returns no rows when a…

Read article →
Software Development

Python Logging Explained: Levels, Handlers, Formatters and Mistakes to Avoid

Python logging explained with code we ran: levels, basicConfig quirks, named loggers, handlers and formatters, logger.exception, lazy formatting, duplicate logs…

Read article →
Software Development

JavaScript Map vs Object: Key Differences, Pitfalls and a Benchmark

JavaScript Map vs Object explained with code we ran in Node.js: key types, ordering, the prototype trap, JSON, size, WeakMap…

Read article →
Software Development

Python Type Hints Explained: Annotations, mypy and the Errors It Catches

Python type hints explained with a real mypy run: basic annotations, Optional and unions, TypedDict, Literal, Protocol, generics, Callable, strict…

Read article →
Artificial Intelligence

LLM Tokens Explained: How AI Models Read Text, Count Costs and Fill Context Windows

LLM tokens explained with a tokenizer we ran: how text is split, why counts differ by language and tokenizer, context…

Read article →
DevOps

Semantic Versioning Explained: MAJOR.MINOR.PATCH, Ranges and Release Rules

Semantic versioning explained with code we ran: MAJOR.MINOR.PATCH rules, pre-releases, correct version sorting, caret and tilde ranges, 0.x versions, lockfiles…

Read article →
DevOps

Git Reset vs Revert: How to Undo Commits Safely, With a Tested Example

Git reset vs revert explained with a repository we built: soft, mixed and hard reset, revert commits, reverting merges, recovering…

Read article →
Software Testing

The Test Pyramid Explained: Layers, Ratios and When to Break the Shape

The test pyramid explained: unit, integration and end-to-end layers, sensible ratios, the ice cream cone anti-pattern, the testing trophy and…

Read article →
Test Automation

pytest Fixtures Explained: Scope, yield Teardown, conftest.py and Factories

pytest fixtures explained with a suite we ran: how fixtures work, scopes, yield teardown, conftest.py, factory fixtures, parametrized fixtures, built-ins…

Read article →
Databases & SQL

SQL Upsert Explained: INSERT ON CONFLICT, MERGE and the REPLACE Trap

SQL upsert explained with queries we ran: INSERT ON CONFLICT DO UPDATE and DO NOTHING, the excluded row, conditional updates,…

Read article →
Databases & SQL

SQL LIKE Operator Explained: Wildcards, Case Sensitivity and Performance

The SQL LIKE operator explained with queries we ran: % and _ wildcards, escaping, NOT LIKE and NULL, case sensitivity…

Read article →
Artificial Intelligence

AI Hallucinations Explained: Why Language Models Make Things Up and How to Reduce It

AI hallucinations explained: what they are, why language models produce them, common types, how to reduce them with grounding and…

Read article →
Test Automation

Mocking in Python: unittest.mock, patch and the Mistakes That Make Tests Lie

Mocking in Python with unittest.mock, tested: Mock and return_value, side_effect, patching where a name is used, spec and autospec, mocking…

Read article →
Software Testing

Code Coverage Explained: Line vs Branch Coverage and What 100% Really Means

Code coverage explained with a real coverage.py run: line, branch and condition coverage, reading a report, why 100% coverage can…

Read article →
Artificial Intelligence

What Are AI Agents? How They Work, With a Simple Agent Loop You Can Run

What are AI agents? A plain guide to the agent loop, tools, memory and guardrails, with a small Python loop…

Read article →
Test Automation

TDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each

TDD vs BDD explained with one discount feature built both ways: red-green-refactor with code we ran, Given-When-Then scenarios, tools, trade-offs…

Read article →
Software Testing

User Acceptance Testing (UAT): Process, Checklist and Real Examples

User acceptance testing explained: who runs it, how it differs from QA, a seven-step process, a UAT test case template,…

Read article →
Artificial Intelligence

What Are Embeddings? Vectors and Cosine Similarity Explained Simply

What are embeddings? A plain-language guide to vectors, cosine similarity and semantic search, with a small example we ran, common…

Read article →
Test Automation

Flaky Tests: What Causes Them and How to Fix Them for Good

Flaky tests explained with demos we ran: the main causes, how to confirm and diagnose one, proper fixes for timing,…

Read article →
Software Testing

Load Testing vs Stress Testing: Differences, Metrics and k6 Examples

Load testing vs stress testing explained: what each proves, spike and soak tests, the metrics that matter, why averages mislead,…

Read article →