Playwright vs Selenium vs Cypress + Framework Architecture
Playwright, Selenium and Cypress all automate browsers, but they're built differently — and those differences decide how fast, stable and flexible your suite will be. This page compares them honestly (including where Selenium and Cypress are the better choice), shows the same test written in all three, and ends with the six-layer Playwright framework architecture interviewers ask you to explain.
At a Glance
| Aspect | Selenium | Playwright | Cypress |
|---|---|---|---|
| How it talks to the browser | W3C WebDriver protocol via a driver per browser (plus BiDi in Selenium 4) | Direct protocol connection to browsers it ships and patches | Runs inside the browser alongside your app |
| Languages | Java, Python, C#, JavaScript, Ruby, Kotlin | JavaScript/TypeScript, Python, Java, .NET | JavaScript/TypeScript |
| Waiting | Explicit waits you write | Auto-waiting built into actions and assertions | Automatic retries built in |
| Browsers | Chrome, Firefox, Edge, Safari (real browsers) | Chromium, Firefox, WebKit (Safari's engine, not Safari itself) | Chrome family, Firefox, Edge; WebKit experimental |
| Tabs / multiple windows | Yes | Yes — pages and contexts | Limited — one tab |
| Parallel runs | Via TestNG/JUnit and Selenium Grid | Built into the test runner | Paid cloud feature or third-party tools |
| Built-in extras | Minimal — you add runner, reports | Test runner, trace viewer, API testing, network mocking | Runner, time-travel debugging, network stubbing |
| Mobile | Real devices via Appium | Device emulation only | Viewport emulation only |
| Best known for | Language choice, real browsers, huge ecosystem | Speed, stability, modern web apps | Developer experience for front-end teams |
The Same Test in All Three
Log in as a locked-out user on SauceDemo and check the error.
Selenium (Java + TestNG)
@Test
public void lockedOutUserSeesError() {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://www.saucedemo.com/");
driver.findElement(By.id("user-name")).sendKeys("locked_out_user");
driver.findElement(By.id("password")).sendKeys("secret_sauce");
driver.findElement(By.id("login-button")).click();
String error = new WebDriverWait(driver, Duration.ofSeconds(10))
.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("[data-test='error']")))
.getText();
Assert.assertTrue(error.contains("locked out"));
} finally {
driver.quit();
}
}
Playwright (TypeScript)
import { test, expect } from '@playwright/test';
test('locked-out user sees an error', async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
await page.locator('#user-name').fill('locked_out_user');
await page.locator('#password').fill('secret_sauce');
await page.locator('#login-button').click();
await expect(page.locator('[data-test="error"]')).toContainText('locked out');
});
Cypress (JavaScript)
describe('login', () => {
it('locked-out user sees an error', () => {
cy.visit('https://www.saucedemo.com/');
cy.get('#user-name').type('locked_out_user');
cy.get('#password').type('secret_sauce');
cy.get('#login-button').click();
cy.get('[data-test="error"]').should('contain', 'locked out');
});
});
Notice what's missing from the Playwright and Cypress versions: an explicit wait and browser clean-up. Playwright's expect(...).toContainText() retries until the text appears, and the runner manages the browser for you. Convert your own snippets between the three in the Selenium ⇄ Playwright ⇄ Cypress Translator.
Playwright vs Selenium
Interview answer: "Both automate browsers, but Selenium drives them through the WebDriver protocol and relies on explicit waits I write, while Playwright connects to the browser directly and auto-waits for elements to be ready. Playwright also ships a test runner with parallelism, tracing and API testing built in. Selenium's strengths are language choice, real branded browsers including Safari, a huge ecosystem and Appium for mobile — which is why many enterprises still run large Selenium suites."
When Selenium is still the better fit
- A large, working Selenium suite — rewriting it rarely pays off quickly.
- Teams that work in Java, C#, Python or Ruby and want the widest language and library support.
- Testing on real branded browsers (for example real Safari) or through cloud grids already in place.
- Mobile testing alongside web through Appium, using the same WebDriver skills.
Detailed comparison: Selenium vs Playwright.
Playwright vs Cypress
Analogy: Cypress is a security guard inside the building — it sees everything happening in its own building very clearly, but can't easily follow someone into a second building (another tab or domain). Playwright is a controller outside the building, able to open and watch several buildings at once.
Interview answer: "Cypress runs inside the browser alongside the application, which gives it an excellent debugging experience, but it's JavaScript-only and limited with multiple tabs and cross-origin flows. Playwright controls the browser from outside, so it handles multiple pages, contexts and origins, supports several languages, and parallelises for free."
When Cypress is a good choice
- Front-end JavaScript teams who want tests next to the application code.
- Single-origin web apps without multi-tab flows.
- Teams that value Cypress's interactive runner and component testing.
Why Many Teams Choose Playwright
- Auto-waiting removes most synchronisation code — the biggest source of flaky UI tests.
- Isolated browser contexts make parallel tests fast and independent.
- Trace viewer shows every step, DOM snapshot and network call of a failed CI run.
- API testing and network mocking in the same tool.
- Three browser engines from one install.
Playwright's Limitations
- No Internet Explorer and no real branded Safari — WebKit is close, not identical.
- No native mobile apps — emulation only; use Appium for real devices.
- A younger ecosystem than Selenium, with fewer third-party integrations in some enterprises.
- Large parallel runs need CPU and memory; plan your CI agents accordingly.
Interview answer: "Playwright isn't the right choice when you need legacy browsers, real Safari or native mobile apps, or when a large, stable Selenium suite already covers the product — the tool should follow the project's requirements, not popularity."
Designing a Playwright Framework: Six Layers
| Layer | Contains | Rule |
|---|---|---|
| 1. Tests | tests/*.spec.ts — scenarios and assertions |
No locators or raw page calls |
| 2. Page objects | pages/*.ts — locators and user actions |
UI changes are fixed only here |
| 3. Fixtures | Custom fixtures that create page objects, logged-in states and data | Setup lives here, not in tests |
| 4. Test data | JSON files and data builders | No hard-coded values in tests |
| 5. Utilities | API helpers, date/random helpers | Reusable and stateless |
| 6. Configuration and reports | playwright.config.ts (baseURL, projects, retries, reporters), traces, screenshots, HTML report |
Environments switch through config, not code |
// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly username: Locator;
readonly password: Locator;
readonly submit: Locator;
readonly error: Locator;
constructor(private readonly page: Page) {
this.username = page.locator('#user-name');
this.password = page.locator('#password');
this.submit = page.locator('#login-button');
this.error = page.locator('[data-test="error"]');
}
async login(user: string, pass: string) {
await this.username.fill(user);
await this.password.fill(pass);
await this.submit.click();
}
}
Why POM? "It separates UI details from test logic, so a changed locator is fixed in one place, tests read like user journeys, and new modules only need a new page class and tests." To log in once and reuse the session across tests, see Playwright Storage State.
The "Biggest Challenge" Answer
- Challenge: flaky failures from dynamic content and slow API responses — tests clicked elements that were visible but not yet updated.
- Action: replaced manual timeouts with web-first assertions (
toBeVisible,toHaveText), waited on specific network responses where needed, used role- and test-id-based locators, and reviewed traces for every CI failure. - Result: a stable suite — give your real numbers, such as "retries dropped from 12% of runs to under 1%".
From Real Projects
I've built my automation skills on Selenium with Java and TestNG, and that background maps well onto Playwright: page classes become page objects, TestNG groups and parallel runs become projects and workers, and explicit waits become auto-waiting and web-first assertions. If you're coming from Selenium too, focus on those differences first. Testing Testsigma — a platform where users write automated tests in plain English — meant thinking like its users, who are testers themselves. Understanding how each tool talks to the browser explains most of their differences.
📚 Official documentation: Playwright documentation · Cypress documentation
FAQs
What is the difference between Playwright and Selenium?
Selenium uses the WebDriver protocol and explicit waits and supports many languages and real browsers; Playwright connects directly to its bundled browsers, auto-waits, and includes a runner with parallelism, tracing and API testing.
What is the difference between Playwright and Cypress?
Cypress runs inside the browser, is JavaScript-only and limited with multiple tabs and origins; Playwright controls browsers externally, supports several languages, multiple pages and contexts, and free parallel runs.
Is Selenium outdated?
No. It remains the standard for multi-language teams, real-browser coverage and mobile via Appium, and Selenium 4 added BiDi and Selenium Manager. Playwright is often faster to write and more stable for modern web apps.
What are Playwright's limitations?
No Internet Explorer, WebKit instead of real Safari, no native mobile apps, and resource-heavy large parallel runs.
How is a Playwright framework structured?
Tests, page objects, fixtures, test data, utilities, and configuration with reports — with setup in fixtures and environments in playwright.config.ts.