Software is a set of instructions that makes a computer do a job. QA checks whether it does the correct job for real people.
Think of testing a bicycle’s brakes and steering before someone rides it.
human need → build software → test behavior → fix problems → release
Series: Playwright Zero to Expert | Demo app used throughout this series: saucedemo.com (UI) and a public REST API (introduced in Part 17)
Before touching a single line of automation code, you need a solid mental model of what you’re actually dealing with — software, websites, and testing. This isn’t filler. Every single confusing moment you’ll hit later — “why did my test fail,” “why is this locator not found,” “why does the UI say one thing and the API says another” — traces back to one of the ideas in this part. So we’re going to go slow and go deep here, because the depth pays off for the rest of the series.
Module 0.1 — What Is Software?
Pick up your phone right now. WhatsApp, Instagram, your bank’s app, the calculator, the camera — every single one of these is software. So is the operating system running underneath them (Android or iOS). So is the code running on Netflix’s servers that decides what to recommend to you next. So is the tiny program inside a washing machine that decides when to spin.
Software is simply a set of instructions that tells a computer (or a chip inside a machine) what to do.
A phone, a laptop, an ATM — without software, they’re just metal and circuits that can’t do anything on their own. Software is what turns that inert hardware into something that responds to you.
Now, people use several related words almost interchangeably in daily conversation, and it’s worth being precise about them, because in a QA interview, precision here signals that you actually understand the field rather than having memorized buzzwords.
- A program is usually a single, focused set of instructions built to do one job. The calculator app on your phone is a program — its only job is arithmetic.
- An application (app) is software built for a user to accomplish some task or set of tasks. WhatsApp is an application — messaging, calling, sharing media.
- A website is an application you reach through a browser, over the internet, by typing or clicking a URL.
amazon.comis a website. - A web application is a website that behaves like a full, interactive application rather than a static page of text — Gmail, Amazon, and your bank’s online portal are all web applications. This is the category almost everything you’ll test with Playwright falls into.
- A mobile application runs natively on a phone (an iOS or Android app you install from an app store) — different from a website viewed on a phone’s browser, even though they can look similar.
- A desktop application is installed and runs directly on a computer — Microsoft Word, VLC media player.
Here’s a distinction that trips a lot of beginners up: opening gmail.com in your phone’s browser is using a web application. Opening the Gmail app you installed from the Play Store is using a mobile application. Same underlying email service, two completely different pieces of software, often built with entirely different technology and — very relevant for you later — tested with entirely different tools. Playwright automates the web application world. It does not automate native mobile apps (that’s a tool like Appium’s job).
The frontend, the backend, and the database — through a real order
Let’s make “frontend,” “backend,” and “database” concrete instead of abstract, using something you’ve almost certainly done: ordering food on an app like Swiggy, Zomato, Uber Eats, or DoorDash.
- When you open the app and see a list of restaurants, browse the menu, tap “Add to Cart,” and hit “Place Order” — everything you’re looking at and touching is the frontend. Buttons, menus, images, the cart icon updating — all frontend.
- The moment you tap “Place Order,” something has to check: is this restaurant still open? Is this address in the delivery zone? What’s the final price after your coupon? None of that logic lives in what you can see — it runs on a server somewhere, invisible to you. That’s the backend.
- And somewhere, permanently, your order needs to be stored — so the restaurant’s kitchen display shows it, so you can see “past orders” tomorrow, so the delivery partner gets assigned to it. That permanent storage is the database.
┌────────────────────┐
You see ───► │ Frontend │ (menu, cart, buttons)
└─────────┬──────────┘
│ talks to
┌─────────▼──────────┘
│ Backend │ (pricing, validation, assignment)
└─────────┬──────────┘
│ reads/writes
┌─────────▼──────────┘
│ Database │ (your order, permanently)
└────────────────────┘
How the Layers Communicate: The Chain of Custody
In modern web applications, these layers do not just sit side-by-side; they pass data along a highly structured chain of custody.
- The Frontend translates user actions (clicks, keypresses) into requests.
- The Backend receives the request, processes the business logic rules, queries the database, formats the result, and returns it.
- The Database acts as the final ledger, ensuring that data is persisted correctly.
📊 Visual Flowchart: Tracing a UI Bug Through the Layers
When an automated test or a manual tester observes a bug (e.g., a product’s price shows as $0.00 on the screen), they must trace the data back through the chain of custody to identify the faulty layer:
graph TD
Bug["UI displays wrong price ($0.00)"] --> DevTools["Open Browser DevTools (F12)"]
DevTools --> NetTab["Inspect Network Tab"]
NetTab --> APIResponse{"Is correct price in API response?"}
APIResponse -->|Yes| FEBug["Frontend Bug<br>(Rendering or formatting error in UI code)"]
APIResponse -->|No| BEOrDB{"Is API request sent correct?"}
BEOrDB -->|Yes| BEBug["Backend or Database Bug<br>(Bad data sent from server)"]
BEOrDB -->|No| NetBug["Network/Request Bug<br>(Client sent incorrect payload)"]
Almost every app you use daily follows this exact three-layer shape: Instagram (frontend = your feed, backend = deciding what to show you, database = every photo and like ever posted), your bank’s app (frontend = balance screen, backend = transaction rules, database = your actual account balance), even a simple to-do list app.
This split matters enormously for testing, and here’s why. Imagine you’re testing SauceDemo and a product’s price shows as $0.00 on screen. A tester without this mental model just says “the price is wrong” and files a vague bug.
A tester with this mental model asks a much sharper question first: is $0.00 what the frontend received and displayed correctly, or did the frontend receive a correct price and simply display it wrong? These are two completely different bugs, owned by two completely different teams, fixed in two completely different ways — and you can only tell them apart by knowing there are three layers to check, not one.
You’ll get hands-on with exactly this kind of investigation once we reach Network tab inspection in Part 1 and API testing in Part 17.
A mistake beginners make constantly, almost without noticing, is assuming every bug they see on screen is a “UI bug” — something wrong with the buttons, the CSS, the layout — because the screen is the only thing they can see. In reality, the UI is very often just an honest messenger, faithfully displaying bad data it received from somewhere deeper. Learning to pause and ask “which layer does this actually belong to?” before touching DevTools is one of the most valuable habits you can build early, and it’s one interviewers specifically probe for.
Interview Questions
Q: What is the difference between software, a program, and an application?
Ans: Software is the umbrella term for any set of instructions that makes hardware do something. A program is usually a small, single-purpose piece of software — like a calculator. An application is user-facing software, often made up of many programs working together, built to accomplish a broader task — like WhatsApp handling messaging, calling, and media sharing all in one app.
Q: Explain frontend, backend, and database in your own words, using a real-world example.
Ans: Using a food delivery app as an example: the frontend is everything the user sees and taps — the restaurant list, the menu, the cart. The backend is the invisible logic that runs when you place an order — checking the restaurant is open, calculating the final price, assigning a delivery partner. The database is where the order is permanently stored, so it can be retrieved later by the kitchen, the delivery partner, or the “order history” screen.
Q: If a webpage shows the wrong price for a product, what could be causing it, and how would you start investigating?
Ans: There are at least three distinct possibilities: the price stored in the database itself is wrong, the backend calculated or fetched it incorrectly before sending it to the browser, or the backend sent the correct price but the frontend displayed it incorrectly (a formatting or rendering bug). The fastest way to narrow this down is to open the browser’s DevTools Network tab, find the API response that carries the price, and check whether the correct price is present in that raw response. If it is, the bug is in the frontend’s display logic. If it isn’t, the problem is further back — in the backend or the database — and that shapes who you report the bug to and how you word it.
Q: Is opening gmail.com in a phone’s browser the same as using the Gmail app? Why does this distinction matter for testing?
Ans: No — opening gmail.com in a browser is using a web application, while the installed Gmail app is a native mobile application. They may look nearly identical and offer the same features, but they’re built with different technologies and tested with different tools. This distinction matters directly for this series because Playwright automates web applications (anything running inside a browser); it does not automate native mobile apps — that requires a different tool, like Appium.
Module 0.2 — What Is a Website?
Imagine the internet as an enormous global postal system connecting every computer in the world to every other computer. A browser — Chrome, Firefox, Safari, Edge — is like a machine sitting on your desk that knows how to write a request, send it out into that postal system, and open whatever comes back in a way you can actually read and interact with.
Every time you want to visit a website, you’re really doing three things, whether you realize it or not:
- You tell the browser an address.
- The browser goes and fetches whatever lives at that address.
- The browser turns what it fetched into the visual page you see.
Let’s break down the pieces properly, using a real address you’ll be using constantly in this series:
https://www.saucedemo.com/inventory.html?sort=az
└─┬─┘ └──────┬───────┘└─────┬──────┘└────┬────┘
protocol domain path query parameter
- URL (Uniform Resource Locator) — the full address above, start to finish. It’s literally the “location” of the resource you want.
- Domain —
saucedemo.com, the core, registered name of the site. This is what a company buys and owns (through a domain registrar). - Path —
/inventory.html, which specific page or resource on that domain you want. - Query parameter —
?sort=az, extra information tacked onto the URL, often used to filter or customize what’s returned (here, telling the site to sort products A-to-Z). - Server — a computer, physically sitting in a data center somewhere in the world, that “serves” (sends back) the website’s content whenever someone requests it. When you visit SauceDemo, you’re not talking to a person or a single machine on someone’s desk — you’re talking to a server, possibly thousands of miles away.
- Client — whatever is making the request. In everyday browsing, the client is your browser. Later in this series (Part 17), you’ll see Playwright act as a client directly, without a browser at all, when it makes raw API calls.
There’s one more piece worth knowing conceptually, because it removes a lot of mystery later: when you type saucedemo.com, your computer doesn’t actually know where that server physically is — computers on the internet find each other using numeric addresses (IP addresses), not friendly names. So there’s a translation step, roughly like looking up a name in a phone book to find the actual number to dial. That “phone book” system is called DNS (Domain Name System).
Analogy: The Internet’s Phone Directory Operator Imagine you want to call a business named “Sauce Labs”. You don’t know their telephone number, so you dial an operator or information service (the DNS Server) and ask: “What is the phone number for saucedemo.com?” The operator looks up the name in their directory, finds the associated number
192.0.2.1(the IP Address), and tells it to you. Now, your phone (the Browser/Client) dials that numeric address directly to connect to the business’s building (the Server).
You don’t need to memorize how DNS works internally for this series, but knowing it exists is enough to understand why, occasionally, a site can be technically “up” but unreachable for you specifically — the phone-book lookup itself failed, before your request even reached the real server.
Once the IP address is known, the request-response dance proceeds as follows:
Client (Browser) Server
───────────────── ──────
1. Look up IP address via DNS ─────────►
2. Get IP address (e.g. 192.0.2.1) ◄──────
3. "Give me saucedemo.com" ──────────────►
4. Server prepares response
◄────────────── 5. Sends back HTML/CSS/JS
6. Browser renders the page
This request-response dance is the single most repeated pattern in everything you’ll do with Playwright. Every page.goto(), every button click that navigates somewhere, every form submission — all of it is this same loop happening again and again. Almost every confusing timeout error you will eventually run into (and you will) comes down to one simple fact: your test checked for something before this loop finished. Understanding this loop now means that error message will make immediate sense later, instead of feeling like a mysterious Playwright quirk.
A subtle but common point of confusion: people often say “the website is down” when, technically, three very different things could have happened — the server itself crashed and isn’t responding at all, the server responded but with an error (like “500 Internal Server Error”), or the request never even reached the server because of a network or DNS problem on the client’s side.
These sound similar from a user’s chair, but they are entirely different bugs with entirely different owners, and being able to tell them apart (again, using the Network tab — you’ll get hands-on with this in Part 1) is a genuinely valuable, fast skill to build.
Interview Questions
Q: What is the difference between a browser and the internet?
Ans: The internet is the global network of connected computers that makes it physically possible for data to travel from one machine to another. The browser is a piece of software running on your device that uses that network to request web content and then displays it to you in a readable, interactive way. The internet is the road; the browser is the car you drive on it.
Q: What is a URL, and what is a domain?
Ans: A URL is the complete address of a specific resource on the web — it can include the protocol, domain, path, and query parameters, e.g. https://www.saucedemo.com/inventory.html?sort=az. A domain is just the core registered name within that URL — saucedemo.com — the part a company actually owns.
Q: In simple terms, describe what happens between typing a URL and seeing the page.
Ans: The browser takes the address you typed, figures out where the actual server lives (via a DNS lookup), sends a request to that server asking for the page, waits for the server to respond, and then takes whatever comes back — typically HTML, CSS, and JavaScript — and renders it into the visual, interactive page you see on screen.
Q: A user says “the website is down.” What are the different things that could actually be happening, and how might you tell them apart?
Ans: It could mean the server crashed entirely and isn’t responding, the server responded but returned an error status, or the request never reached the server at all due to a network or DNS issue on the client’s side. Opening the browser’s DevTools Network tab while reproducing the issue is the fastest way to tell these apart — you can see directly whether a request was sent, whether a response came back, and what status code that response carried.
Module 0.3 — How Web Applications Work
Now let’s zoom in one more level on that browser-server exchange, because this is the model you’ll return to constantly for the rest of the series — especially once we reach API testing in Part 17 and database verification in Part 22.
User
│ clicks / types
▼
Browser
│ sends a request
▼
Internet
│ carries the request
▼
Server
│ runs the application logic
▼
Application (Backend)
│ reads / writes data
▼
Database
│ returns data
▼
Response travels back up through the same chain
▼
Browser renders the final result
Let’s walk through this with a real, concrete example — logging into SauceDemo, since it’s the app you’ll be testing throughout this entire series.
- You open
saucedemo.comand land on the login page. Your browser has already gone through the request-response loop from Module 0.2 to get this far. - You type a username and password and click Login.
- Your browser bundles that username and password into a new request and sends it off to SauceDemo’s server.
- The server’s backend logic wakes up and does the actual thinking: does a user with this exact username and password exist? Are they allowed to log in right now (not banned, not locked out)?
- To answer that, the backend asks the database directly: “do you have a record matching this username and password?”
- The database checks its stored records and replies — either “yes, here’s the matching user” or “no match found.”
- The backend takes that answer and decides what response to send back to the browser: if the login is valid, it prepares data for the next page (the product inventory) and often a token proving you’re logged in (we’ll dig into this properly in Part 18 — Authentication). If it’s invalid, it prepares an error message instead.
- That response travels back across the internet to your browser.
- Your browser receives it and renders the result — either redirecting you to the inventory page full of products, or showing you “Username and password do not match any user in this service.”
Notice something important: your browser did almost no “thinking” in this whole sequence. It sent a request and displayed whatever came back. Nearly all the actual decision-making — is this login valid? — happened invisibly, on the server, several steps away from anything you could see with your eyes on the page.
This is exactly why, when something goes wrong, “look harder at the page” is often the wrong instinct. If SauceDemo said “login failed” for credentials you’re sure are correct, staring at the login form itself won’t tell you anything new — the form did its job correctly by sending the request. The real answer is sitting inside the response the server sent back, which is only visible through tools like the Network tab (Part 1) or, once we get there, Playwright’s own request-interception abilities (Part 19).
One more thing worth internalizing here: a single page load is almost never one request. Opening saucedemo.com/inventory.html alone typically involves separate requests for the HTML structure, the CSS styling, JavaScript code, each product image, and often a separate API call just to fetch the list of products as data.
They don’t all finish at the same instant — some are faster, some slower, some might even fail independently of the others. This single fact is the root cause of most timing-related bugs you’ll encounter once you start writing real Playwright tests, and it’s why “waiting” gets an entire dedicated part later (Part 12) instead of being a quick afterthought.
Interview Questions
Q: Walk me through what happens, step by step, when a user logs into a web application.
Ans: The browser sends the entered credentials to the server as a request. The server’s backend logic checks whether those credentials are valid, typically by querying the database for a matching record. The database returns either a match or no match. Based on that, the backend prepares a response — either data for the next page plus proof of a valid session, or an error message — and sends it back. The browser receives that response and renders the outcome, either navigating to the next page or displaying the error.
Q: A page shows “No products found” but you believe there should be products. How would you figure out which layer the problem is in?
Ans: Open the Network tab, find the request responsible for fetching product data, and inspect its response directly. If the response itself comes back empty, the problem is upstream — likely the backend logic or the database has no data to return. If the response actually contains product data but the page still shows nothing, the problem is in the frontend’s rendering logic, which is failing to display data it clearly received.
Q: Where would Playwright fit if you wanted to verify both the UI and the underlying API response for a scenario like this?
Ans: Playwright can do both in a single test. It can interact with and assert on what’s visually rendered in the browser (standard UI automation), and separately, using its built-in request context, it can call the same API endpoint directly and assert on the raw JSON response — letting you confirm not just that “products appear on screen” but that the correct data was actually returned by the backend in the first place. We’ll build exactly this kind of combined test in Part 17.
Q: Why is a single page load rarely just one request, and why does this matter for testing?
Ans: A typical page load involves multiple separate requests — the base HTML, CSS files, JavaScript files, images, and often one or more API calls for dynamic data — all firing independently and finishing at different times. This matters enormously for testing because “the page has loaded” is ambiguous: the HTML structure might be present while the actual product data is still loading. Tests that don’t account for this can check for something before it’s actually ready, causing intermittent, hard-to-explain failures — a problem serious enough to get its own dedicated part later in this series.
Module 0.4 — Software Testing
Every piece of software is written by people, and people make mistakes — a mistyped condition, a missight case, a misunderstanding of what the requirement actually asked for. A bug (or defect) is simply any behavior of the software that doesn’t match what it’s actually supposed to do.
Think about times you’ve personally hit a bug without calling it that: a “like” button on Instagram that didn’t register the first tap, a food delivery app that let you apply the same discount coupon twice, an online form that let you submit a payment with an empty card number field. Every one of those is a bug — a mismatch between intended behavior and actual behavior.
Testing exists to find these mismatches before real users do.
That single sentence is the entire justification for the QA profession. A bug found by a tester, in a controlled environment, before release, costs a company a bug-fix. A bug found by ten thousand paying users, in production, after release, can cost a company its reputation, its revenue, and in extreme cases (think banking or medical software), someone’s real money or safety. The earlier a bug is caught, the cheaper it is to fix — a well-known pattern in software engineering, and one of the strongest arguments for automation, since automation lets the same checks be repeated constantly, catching regressions the moment they’re introduced rather than weeks later.
Let’s ground the core vocabulary in something concrete — testing the SauceDemo login page.
- A test scenario is a high-level idea of what needs to be verified, before it’s broken into precise steps. Example: “Verify that login works correctly with valid credentials.” Another: “Verify that login is rejected with invalid credentials.”
- A test case takes that scenario and makes it exact and repeatable — specific steps, specific input data, and a specific expected result:
| Step | Action | Data | Expected Result |
|---|---|---|---|
| 1 | Navigate to saucedemo.com | — | Login page is displayed |
| 2 | Enter username | standard_user | Username field shows entered value |
| 3 | Enter password | secret_sauce | Password field shows masked value |
| 4 | Click Login | — | User is redirected to the inventory page |
One scenario (“verify login works”) can easily produce several test cases — one for a standard user, one for a locked-out user, one for an empty password field, and so on.
- Expected result is what should happen according to the requirements — “user is redirected to the inventory page.”
- Actual result is what genuinely happened when you ran the steps.
- The test fails the moment expected and actual don’t match — say, the user stays on the login page with no error message shown at all, which itself would be a bug worth reporting.
Manual testing
is a human physically clicking through these steps, comparing what they see against what’s expected, every single time. Automation testing is code — Playwright, in our case — performing those exact same steps and comparisons, without a person repeating them by hand.
Picture doing the SauceDemo login test case above by hand, once a day, for a year, across five different browsers, for fifty different login scenarios (valid user, locked-out user, wrong password, empty fields, SQL-injection-style input, and so on). That’s not a job for a human doing it a hundred times a week — it’s tedious, error-prone (humans get careless doing repetitive tasks), and slow.
That exact tedium is precisely the gap Playwright and automation testing exist to fill: write the check once, correctly, and then run it as many times as you want, at machine speed, without ever getting bored or careless halfway through.
That said, it would be a real misunderstanding to conclude automation makes manual testing obsolete. Automation is excellent at repeating an exact, well-defined check reliably — that’s regression testing’s whole purpose.
But a human, casually poking around an app without a rigid script, will notice things no automated test was ever told to look for: a button that feels laggy, text that’s technically correct but confusingly worded, a layout that looks fine on a laptop but breaks on a smaller screen nobody thought to test.
That kind of open-ended, judgment-driven testing is called exploratory testing, and it remains a genuinely valuable, distinctly human skill even in the most heavily automated QA teams — automation and exploratory testing are complements, not competitors, and we’ll come back to this trade-off explicitly later in the series.
It’s also worth being honest about something beginners often get backwards: a failing automated test does not automatically mean the application has a bug. It might — but it could just as easily mean the test itself is wrong. Maybe the expected value in the test is outdated after a legitimate, intentional UI change.
Maybe the locator the test uses to find an element is fragile and broke because of an unrelated CSS change. Maybe the test checked for something a half-second too early, before the page had actually finished loading.
Learning to investigate a red test with genuine curiosity — “is this the app, or is this the test?” — rather than assuming one or the other, is a habit that will save you enormous amounts of wasted time once you’re maintaining a real suite.
Interview Questions
Q: What is a bug? What is the difference between expected and actual result?
Ans: A bug is any behavior of the software that doesn’t match what it’s supposed to do according to its requirements. The expected result is what should happen based on those requirements; the actual result is what genuinely happens when the software is used or tested. A bug exists precisely where those two disagree.
Q: What is the difference between a test case and a test scenario?
Ans: A test scenario is a broad, high-level statement of what needs to be verified — for example, “verify login works with valid credentials.” A test case takes that idea and makes it concrete and repeatable, with exact steps, exact input data, and a precisely stated expected result. A single scenario commonly expands into several distinct test cases.
Q: Why does automation testing exist if manual testing already works?
Ans: Manual testing works, but it doesn’t scale well — repeating the same precise steps across many scenarios, browsers, and releases becomes slow, tedious, and prone to human error over time. Automation testing encodes those checks once as code, then runs them repeatedly at machine speed and with perfect consistency, which is exactly what’s needed for regression testing across a fast-moving codebase. It doesn’t replace manual testing entirely, though — exploratory, judgment-driven testing still catches issues no automated script was ever told to look for.
Q: A Playwright test fails. What are the possible reasons, beyond “the application has a bug”?
Ans: Several things besides a genuine application bug can cause a test to fail: a broken or overly fragile locator that stopped matching an element after an unrelated UI change, a timing issue where the test checked for something before the page had actually finished loading, stale or already-used test data, an environment-specific issue, or simply an outdated expected value in the test itself after an intentional, legitimate change to the application. A good habit is to investigate a failure with real curiosity about which of these it is, rather than assuming either the app or the test is automatically at fault.
Module 0.5 — Types of Testing
Not all testing looks the same, because not all testing is trying to catch the same kind of problem, at the same layer, at the same speed. Understanding this landscape is what lets you explain — convincingly, in an interview or on a real team — why an organization needs more than “a bunch of UI tests.”
| Type | What it checks | Speed | SauceDemo-style example |
|---|---|---|---|
| Unit | A single small piece of code, in isolation | Very fast | Does the function that calculates a cart’s total return the correct sum for a given list of items? |
| Integration | Multiple pieces of code working together | Fast | Does the cart module correctly call the pricing module and receive the right value back? |
| API | The backend’s request/response behavior, with no UI involved at all | Fast | Does POST /login return a valid session token for correct credentials, and a proper error for incorrect ones? |
| UI | An individual UI component or interaction | Medium | Does clicking “Add to Cart” correctly update the cart icon’s item count? |
| End-to-End (E2E) | A complete, realistic user journey across the whole system | Slow | Can a user log in, browse products, add one to the cart, and complete checkout, start to finish? |
| Regression | Re-confirming that previously working features still work after a new change | Varies | After a checkout bug fix, re-running the entire login suite to make sure nothing else broke |
| Smoke | A small, fast set of checks confirming the build isn’t fundamentally broken | Very fast | Can the app even load, and can a standard user log in at all? |
| Sanity | A narrow, focused check after a specific fix, without a full regression run | Fast | After fixing a bug in the checkout total calculation, quickly re-verifying just that calculation |
It helps to actually picture these layered on top of each other, because that shape — not just the list — is what interviewers are really testing when they ask about it:
▲
╱ ╲
╱ E2E╲ few tests, slow, expensive, high confidence
╱───────╲
╱ API ╲ more tests, faster, focused on business logic
╱─────────────╲
╱ Unit Tests ╲ most tests, fastest, cheapest, narrowest scope
╱───────────────────╲
This shape is called the testing pyramid, and the reasoning behind it is worth actually internalizing, not just memorizing as a diagram. Unit tests are cheap to write, run in milliseconds, and pinpoint exactly which function broke — so you want a large number of them, forming a solid base.
E2E tests, by contrast, spin up a real browser, load a real page, wait for real network calls, and click through a real multi-step journey — they’re slow by comparison, more fragile (more moving parts means more ways for something unrelated to cause a failure), and expensive to maintain — so you want relatively few of them, reserved specifically for the handful of journeys that genuinely need to be verified as a whole, working system (like “can a user actually complete a purchase,” which is exactly the kind of thing no unit test alone could ever prove).
A very real, very common anti-pattern — one interviewers genuinely like probing for — is a team that inverts this pyramid: dozens or hundreds of slow, brittle E2E tests, and almost no unit or API tests underneath. This tends to happen naturally, because E2E tests feel the most “realistic” and satisfying to write early on.
The consequence is a test suite that takes hours to run and fails unpredictably, because with that many moving browser-driven parts, something is statistically likely to be flaky on any given run — and a team in that position often can’t tell a real regression from noise anymore. We’ll come back to this exact failure mode with real fixes in Part 28 (Flaky Tests).
Where does Playwright actually sit in this picture?
Primarily at the UI and E2E layers — it drives a real browser and simulates real user journeys, which is exactly what those top layers need. But Playwright also ships a built-in request API capable of making raw HTTP calls without a browser at all, which means the same tool can also cover the API layer. This is one of the biggest reasons Playwright has become so popular with QA teams: instead of maintaining Selenium for UI and a separate tool (like Postman scripts or a different framework) for API tests, many teams now cover both layers with one consistent tool, one language, and one reporting pipeline.
QA roles in a real team
As this series moves you from “never written a test” toward “can design a production framework,” it’s worth knowing what role you’re actually growing into, because the titles map roughly onto depth of skill:
- A Manual QA / Tester executes test cases by hand, performs exploratory testing, and writes clear bug reports. This is often where people start, and the judgment built here (what to test, how to describe a bug precisely) stays valuable forever, even after you automate.
- An Automation Engineer writes and maintains automated test scripts — this is the core skillset this entire series is built to give you, from Part 6 onward.
- An SDET (Software Development Engineer in Test) is an automation engineer with strong software-engineering ability — someone who doesn’t just write individual tests, but builds the frameworks, shared utilities, and tooling that an entire team’s tests run on. This is roughly where Parts 15, 20, and 29–31 are aiming you.
- A Test/QA Architect designs testing strategy and framework architecture across an entire organization — deciding what should be unit-tested versus E2E-tested, how CI/CD should be structured, how a thousand-test suite should stay fast and reliable. This is the altitude Parts 39–41 are ultimately building toward.
Interview Questions
Q: What is the difference between smoke testing and sanity testing?
Ans: Smoke testing is broad but shallow — a small set of checks run on every new build to confirm the application isn’t fundamentally broken (it loads, login works, core navigation functions). Sanity testing is narrow but focused — a quick, targeted check performed after a specific bug fix, verifying just that area, without running a full regression suite.
Q: What is regression testing, and why does it matter?
Ans: Regression testing means re-running previously passing tests after a change, to confirm that the change didn’t unintentionally break something that used to work. It matters because software is interconnected — a fix or feature in one area can easily have unexpected side effects elsewhere, and regression testing is what catches those side effects before they reach real users.
Q: Explain the testing pyramid in your own words.
Ans: The testing pyramid is a way of describing how many tests you should have at each layer of a system: a large number of fast, cheap unit tests at the base, a smaller number of API/integration tests in the middle, and a small number of slow, expensive but high-confidence end-to-end tests at the top, reserved for critical full user journeys. The shape reflects a trade-off — lower layers are faster and pinpoint issues precisely, while higher layers are slower and more fragile but prove the whole system genuinely works together.
Q: Where does Playwright fit in the testing pyramid, and why can it fit in more than one layer?
Ans: Playwright primarily sits at the top of the pyramid, driving real browsers to simulate complete user journeys for UI and E2E testing. But it also includes a built-in request API that can make raw HTTP calls directly, without a browser — which lets it also cover the API testing layer. That dual capability is a major reason many QA teams standardize on Playwright as a single tool spanning two layers of the pyramid instead of maintaining separate tools for each.
Q: Your team only has E2E tests — no unit or API tests — and the suite takes 3 hours to run and fails randomly 15% of the time. What would you recommend, and why?
Ans: I’d push to rebalance the pyramid rather than just accepting the situation — moving pure business-logic checks down into unit or API tests, which run far faster and are inherently more stable since they don’t depend on a real browser, network timing, or rendering. E2E tests would be reserved specifically for the small number of journeys that genuinely need whole-system verification, like completing a purchase end to end. I’d also treat the 15% flake rate as a problem to genuinely diagnose — is it timing, shared test data, environment instability — rather than simply adding retries, since retries hide the real issue instead of fixing it.
Q: How would you decide, for a new feature, which type(s) of testing it actually needs?
Ans: I’d start by asking what could go wrong at each layer independently. If the feature involves a calculation or a piece of standalone logic, that belongs in a unit test, since it’s the fastest and most precise place to catch a mistake. If it involves a new API endpoint or a change in backend behavior, that needs an API test. If the feature’s real risk is in how several parts of the system behave together as a full journey — like a new checkout step — that’s what justifies an E2E test. I wouldn’t default to “add an E2E test” for everything, since that’s exactly how suites end up slow and top-heavy.
How It Works in a Real Test Run
Software becomes testable when a requirement can be observed as input, behavior, and expected output. A QA engineer turns a human statement such as “a locked user cannot buy an item” into setup, action, evidence, and a clear pass-or-fail check.
A real feedback loop is: requirement → test idea → test execution → evidence → defect or confidence. Automation makes repeated execution faster, but people still decide what risk matters and whether the check proves the intended behavior.
Exercises — Part 0
Understand: Without looking anything up, write your own one- or two-sentence definitions for: frontend, backend, database, browser, server, bug, test case, test scenario. Then compare what you wrote against this part — where were you fuzzy?
Simple Practice:
Open saucedemo.com, open DevTools (F12) → Network tab, reload the page, and log in with standard_user / secret_sauce. Just watch the list of requests fire — don’t worry about understanding each one yet. Count roughly how many separate requests happen just to load the login page, and then again for logging in and reaching the inventory page.
Real-World Scenario: Imagine you log into SauceDemo and the product list is empty, even though you’re sure there should be products. Write down, step by step, exactly how you’d investigate — which tool or tab you’d open first, what specifically you’d look for, and how you’d decide whether the issue is frontend, backend, or database.
Challenge: Pick any app you personally use daily — banking, food delivery, social media, anything. Identify one thing on it that’s clearly frontend (visual/interactive, no real logic needed), one thing that clearly requires backend logic (can’t just be static content — some decision has to be made), and one thing that clearly requires a database (data specific to you, that persists across sessions). Then, for one feature of your choosing, decide which type(s) of testing from Module 0.5 you’d want covering it, and briefly justify why.
Next: Part 1 — Web Fundamentals (HTML, CSS, DOM, DevTools, Selectors, XPath)
— the vocabulary you need before we can talk about how Playwright actually finds things on a page.
- Author
- TechByteByByte Editorial Team
- Reviewed by
- TechByteByByte Admin
- Published
- Last reviewed