Interactive resource
The QA Career Roadmap.
The path I wish someone had handed me in 2015: from your first manual test to AI-augmented automation and team leadership, in seven stages. Tick off each checkpoint as you grow, and your progress saves automatically. Every stage links free resources, the role you're growing into and the tools that matter now, and the coding stage comes with drills you can run right here.
Foundations
Think like a tester
Before tools and code, testing is a way of looking at software: what it should do, and every place it might quietly break. This stage builds the mindset and the vocabulary.
- Sketch the phases: requirements, design, build, test, release, maintain, and mark where QA touches each, not just the "test" box.
- Learn the bug life cycle: New, Assigned, Open, Fixed, Retest, Verified or Reopened, Closed.
- Contrast Waterfall with Agile, where you test every sprint and join standups, planning and retros.
Try thisTake "user can reset their password" and trace it through every phase: in requirements you ask what makes a valid password; in test you check the reset link expires.
"Shift left" just means catching problems as early as possible. The cheapest bug to fix is the one you catch reading the requirements, before any code exists.
JiraConfluenceMiro- For every input, run the destroy-it list: empty, whitespace, very long, special characters, emoji, negatives, injection.
- Hold three questions: does it do the right thing, does it fail gracefully when misused, does it hold up under stress.
- Treat every assumption ("users only type numbers here") as a test to run, not a fact to trust.
Try thisOpen any login form and submit an empty password, a 256-character password, one of only spaces, and one full of emoji. Note what each does. That is the QA mindset in miniature.
Your turnPick one app you use daily (a banking app, a food-delivery app, a note app). Write down five ways a normal person could break one single screen of it, without thinking about code at all.
Show the answer
There is no wrong answer here, but a strong list mixes inputs and conditions: paste 1,000 characters into a name field; lose signal mid-submit; rotate the screen on step 2 of 3; tap the button twice fast; set the system date to 2099. If your five are all "type bad text", push for variety: timing, network, device state, and repetition are where the real bugs hide.
Bugs cluster. Find one in a module and slow down, there are almost always more nearby. "That is weird, let me check why" starts every great bug report.
Browser DevToolsBugMagnetA notebook- Ask of each story: is this testable? If you cannot write a pass/fail check, it is too vague.
- Hunt weasel words: "fast", "user-friendly", "should handle errors", "etc." Each hides an untested assumption.
- Confirm the acceptance criteria are complete and that no two stories contradict each other.
Try thisFor "user can upload a profile photo", list what it does not say: max size, allowed formats, what happens with a 0-byte file, a 50 MB file, or a .exe renamed to .jpg. Bring those to refinement.
Your turnHere is a real-sounding story: "As a user, I can apply a coupon code at checkout so I get a discount." List five questions you would ask before you could test it.
Show the answer
Strong questions attack the gaps the story leaves open: Is the code case-sensitive? Can two codes stack? What happens to an expired or already-used code? Is there a minimum spend? Does the discount apply before or after tax and shipping? Each question you ask in refinement is a bug you prevent before a line of code is written.
A question asked in refinement is a bug prevented. Turning vague stories into sharp acceptance criteria is one of the most valuable things a tester can do.
JiraConfluenceGiven-When-Then- Use equivalence partitioning: group inputs that behave the same, then test one value per group.
- Use boundary value analysis: test the edges (min, min-1, max, max+1), because bugs live at boundaries.
- Reach for decision tables when behaviour depends on combinations, and state transitions for flows.
Try thisA field accepts ages 18 to 60. Instead of infinite inputs, test 17, 18, 19, 59, 60, 61, plus 0, -1 and "abc": about nine high-value cases.
Your turnA password field requires 8 to 16 characters. Using boundary value analysis, write down exactly which lengths you would test, and why each one earns its place.
Show the answer
Test lengths 7 (just below min), 8 (min), 9 (just above min), 15, 16 (max), 17 (just above max), plus 0 (empty). That is seven cases covering every edge where the rule flips from invalid to valid and back. You do not need to test 11 or 12; they sit safely inside the valid partition and tell you nothing new. This is exactly why good testers find more with fewer tests, and it comes up in nearly every interview.
These techniques are why good testers find more bugs with fewer tests, and they come up in almost every interview. Know boundary and equivalence cold.
Sheets / ExcelTestRailPICT (pairwise)- Start with a charter: a mission like "explore checkout with invalid coupon codes", not random clicking.
- Time-box it and keep running notes: what you did, what you expected, what happened, new questions.
- Follow surprises before returning to the charter, and use heuristics to cover an area systematically.
Try thisCharter: explore the cart by changing quantities. Try 0, -1, 9999, 1.5, then add the same item in two tabs. 9999 of a $40 item may overflow the price or let you buy out-of-stock.
Exploratory testing is structured improvisation, not guessing. Scripts confirm what you expect to work; exploration finds what nobody thought to specify.
Session-based test mgmtXmindLoom / OBS- Separate functional (does it do the right thing) from non-functional (performance, usability, security, accessibility).
- Tell smoke (does the build basically work) from sanity (does this one fix work) from regression (did we break anything).
- Learn black-box vs white-box so you can describe how much of the system you can see.
Try thisAfter a payment hotfix: run a smoke test (app loads, checkout reachable), a sanity test (the discount code now applies), then regression on cart totals, tax and the confirmation email.
Smoke is wide and shallow (whole build, surface level); sanity is narrow and deep (one area, after a change). Keeping the two straight saves a lot of confusion when a build lands.
TestRailZephyrBrowserStack- Include the essentials: a specific title, numbered repro steps, expected vs actual, environment, severity and priority.
- Write the title so a developer can triage without opening it: what is broken, where, under what condition.
- Attach evidence: annotated screenshot, screen recording, console errors and the request/response.
Try thisNot "login broken" but "Login fails with 500 when email contains a + character (Chrome 125, build 4.2.1)", with numbered steps and a HAR file attached.
Your turnRewrite this bug title so a developer can triage it without opening the ticket: "Search is broken". Assume the search returns nothing only when you include an apostrophe, in Safari.
Show the answer
Something like: "Search returns 0 results for any term containing an apostrophe (e.g. \"O\u2019Brien\"), Safari 17 only". It names what is broken, the exact trigger, and the environment. A developer can guess the cause (unescaped quote in the query) before they even open it. That is the bar: a report so clear it never bounces back as "cannot reproduce".
The bar: a developer reproduces your bug without messaging you once. Reports so clear they never bounce back as "cannot reproduce" earn dev trust fastest.
JiraDevTools (Network/Console)Loom- Learn the hierarchy (Epic, Story, Task, Bug) and how issues link to each other.
- Walk a ticket across your team’s board and learn what each column actually means.
- Use JQL to find your work, like
assignee = currentUser() AND status = "In QA".
Try thisPick up a story in "In QA", find a defect, log a Bug linked to that story with "is caused by", move the story back, and @-mention the developer.
Being the junior who can instantly pull "all open High bugs in this sprint" makes you the person the lead leans on during release crunch.
JiraJQLXray / Zephyr
Core skills
Test across surfaces
Real products live on the web, on phones and behind APIs. This stage turns a manual tester into a versatile QA who can test any surface a user touches.
- Tell native, hybrid and mobile-web apps apart, because each one fails differently.
- Test mobile-only conditions: rotation, interruptions (a call mid-action), low battery, airplane mode, backgrounding.
- Check gestures and touch-target size, on real devices and changing networks, not just an emulator.
Try thisOn a multi-step signup, rotate the device at step 2 and confirm nothing is lost; then switch to airplane mode as you tap Submit and see whether it warns you cleanly or just spins forever.
Emulators are for speed; real devices catch the bugs that matter. Always test one low-end Android, because that is where performance problems hide.
BrowserStackAndroid Studio / XcodeCharles Proxy- Learn the request anatomy: method, URL, headers, body.
- Memorise the status families: 2xx success, 3xx redirect, 4xx your request is wrong, 5xx their server crashed.
- Know the key headers: Content-Type, Authorization, Set-Cookie, Cache-Control.
Try thisOpen DevTools Network, log in somewhere, and find the login request: a
POST, credentials in the body, a200withSet-Cookie. Now use a wrong password and watch it return401.Your turnMatch each status code to what it tells you, and who needs to fix it: 200, 301, 401, 403, 404, 500.
Show the answer
200OK (it worked).301moved permanently (a redirect; check the new URL).401unauthorized (you are not logged in; auth problem).403forbidden (you are logged in but not allowed; permissions problem).404not found (wrong URL or deleted resource).500server error (their code crashed, not your request). The4xxfamily means your request is wrong; the5xxfamily means their server is. Knowing which is which tells the developer where to even start looking.Knowing status codes by heart is a superpower: 4xx means your request is wrong, 5xx means their code crashed. That alone tells the dev where to look.
DevTools (Network)Postmanhttpstatuses.com- Send a request: pick the method, add headers and auth, then read the status, time and JSON body.
- Organise into collections and parameterise with variables like
{{base_url}}and{{token}}. - Add assertions and chain requests: capture a token or id from one response into the next.
Try thisSend
GET https://reqres.in/api/users/2and confirm200withdata.id2. Then register without a password and confirm400"Missing password". Save all of it as a collection.Learn Bruno alongside Postman: its collections live as plain files in Git and show up in pull-request diffs like code, which is exactly what teams now want.
PostmanBrunoNewmanreqres.in- Verify status, response time, Content-Type, and that the JSON schema matches the contract.
- Push the negatives: missing fields, wrong types, oversized and malformed payloads should return a clean 4xx, not a 500.
- Test auth: no token (401), expired token, and a valid token for the wrong user (should be 403).
Try thisFor
GET /api/orders/{id}: a valid id returns200, a missing id404, no auth401, and another user’s id with your token should be403. If it returns the order, you found a broken-access-control bug.The highest-value API bugs are authorization holes: can user A read user B’s data by changing one id? Always test "valid token, wrong owner".
Postman / BrunoJSON Schema validatorSwagger- Use Elements to read the DOM and grab a stable selector (id, class or data-testid).
- Use Console to catch JavaScript errors that signal hidden bugs even when the UI looks fine.
- Use Network to inspect the calls, payloads and failures behind a button click.
Try thisA Submit button does nothing. DevTools shows a Console TypeError and no request in Network, so your report becomes "Submit throws a JS error and never calls the API", which a dev can act on in seconds.
Reading the Console and Network tabs is the biggest single upgrade a manual tester can make, and it is the exact skill you will reuse for automation locators.
Chrome DevToolsSelectorsHubFirefox DevTools- Run an automated scan (axe or Lighthouse), then navigate the whole flow with only the keyboard.
- Check colour contrast against WCAG AA (4.5:1) and that images have alt text and fields have labels.
- Run a Lighthouse audit for Core Web Vitals and throttle to Slow 3G to feel the real experience.
Try thisTry to complete a checkout using only the keyboard: Tab through every field, no mouse. If you cannot reach "Place Order", that is a blocking accessibility defect.
Automated tools catch only about a third of accessibility issues. The keyboard-only pass and a screen reader find the rest, and accessibility is increasingly a legal requirement.
axe DevToolsLighthouseWAVEVoiceOver / NVDA- Write a light test plan: scope, approach, environments, entry and exit criteria, risks, and what you will not test.
- Write cases with id, preconditions, numbered steps, test data and expected result, specific enough for anyone to run.
- Organise cases into suites (smoke, regression, feature) and trace them back to requirements.
Try thisFor "reset password": precondition account exists and logged out; steps from Forgot password to setting a new one; expected: new password works, old one rejected. Then add expired-link and mismatch cases.
Write cases so a brand-new teammate could run them with zero context, because in six months that teammate is you. Do not over-script, pair them with exploration.
TestRailXrayZephyr ScaleSheets- Take one feature and test it at the UI, the API, and the database, then confirm the three agree.
- Run the full sweep on it: functional, negative, boundary, a short exploratory charter, then a11y and performance.
- Log findings in Jira with linked cases and a short coverage-and-risk summary.
Try thisFor "add to cart": UI badge increments,
POST /cartreturns the right item, andSELECT * FROM cart_items WHERE user_id = 42shows the row. Then add9999and the same item from two tabs and watch the layers disagree.The bugs nobody else finds live in the seams between layers: the UI says "added", the API succeeded, but the database never committed. Basic SQL is now a near-universal junior requirement.
PostmanDBeaver + SQLDevToolsJira
Code foundations
Learn to program
You do not need a computer science degree, but automation runs on code. This is the smallest slice of real programming that lets you read, write and fix a test. Learn it by running code, not just reading about it. Every checkpoint here has a drill you can run in the playground below.
- Choose one and commit: JavaScript / TypeScript pairs with Playwright and the web; Python pairs with pytest. Do not learn two at once.
- Install Node.js (or Python), install VS Code, and learn to run a file from the integrated terminal.
- Get your first line of output: a program that prints a message and runs without an error.
Try thisYour whole setup is proven the moment
console.log("hello")runs in the terminal and prints hello. That is a working toolchain; everything else is building on it.Your turnOpen the playground below, select the "Hello, output" drill, and make it print your name and a number on two separate lines.
Show the answer
Two
console.logcalls do it:console.log("Julia");thenconsole.log(2026). Each call prints its own line. The point is not the code, it is proving your hands know the loop: type, run, read the output, repeat. That loop is 90% of learning to program.Do not try to learn the whole language first, you will stall for months. Learn enough to read and modify a test, then learn the rest on demand.
Node.jsVS CodePythonThe terminal- Learn the everyday types: string, number, boolean, null and undefined, and how to store each in a variable.
- Understand why type matters in testing: "5" (text) and 5 (number) look the same on screen but behave differently in code and in an API.
- Use
typeofto check what you actually have, and template literals to build strings cleanly.
Try thistypeof "5"is"string"andtypeof 5is"number". An API that returns"age": "30"instead of"age": 30is a real contract bug, and you can only spot it once you understand types.Your turnIn the "Types" drill, log the typeof for: a name, an age, whether a user is active, and a price written as "19.99" in quotes. Which one is secretly the wrong type for maths?
Show the answer
The price
"19.99"is a string, not a number, so"19.99" * 2will not behave like money (it may giveNaNor a surprise). Knowing the difference between"19.99"and19.99is exactly the kind of bug that ships to production when nobody checked the type the API actually returns.Half of all "weird" automation failures are a value being a string when you expected a number, or
nullwhen you expected a value. Check the type first, always.JavaScripttypeofTemplate literals- Learn arrays (an ordered list, like a page of search results) and how to read items by index.
- Learn objects (a labelled record, like one user) and how to read fields with dot and bracket notation.
- Combine them: almost every JSON response is an array of objects, sometimes nested several levels deep.
Try thisresponse.users[0].emailreads the email of the first user. Ifusersis empty,users[0]isundefinedand.emailthrows, which is the single most common error a new automator hits.Your turnIn the "Arrays & objects" drill you are given a list of user objects. Print just the email of every user, one per line.
Show the answer
Loop or map over the array and read the field:
users.map(u => u.email).forEach(e => console.log(e)). Or a simple for-of loop:for (const u of users) console.log(u.email). Reading a field out of every object in a list is the bread and butter of API testing, so get this one into your fingers.Before you assert on
response.data[0], assert the array is not empty. "Cannot read property of undefined" is almost always an empty list you did not check for.ArraysObjectsJSONmap / forEach- Use a loop (for or for-of) to do the same check across many items instead of copying code.
- Use
if / else if / elseto branch, and learn===(strict equals) over==to avoid type-coercion traps. - Write a function that takes inputs and returns a value, so your logic has a name and can be reused.
Try thisA function
isValidAge(n)returnsn >= 18 && n <= 60. Now your boundary cases from stage 1 become real, runnable checks instead of notes in a doc.Your turnIn the "Functions & logic" drill, finish isValidAge(age) so it returns true only for 18 to 60 inclusive, then run it against 17, 18, 60 and 61 and print each result.
Show the answer
return age >= 18 && age <= 60. Running it printsfalse, true, true, false. Notice you just turned boundary value analysis into executable code: the same edges you would test by hand are now a function you could call ten thousand times. That mental jump, from manual checks to repeatable code, is the whole point of this stage.Use
===not==in JavaScript.==quietly converts types, so0 == ""is true and you will chase a "passing" test that is actually comparing the wrong things.for / for-ofif / elseFunctions===- Understand sync vs async: some work (a network call, a click) finishes later, not on the next line.
- Learn that an async call returns a promise, a placeholder for a value that is not ready yet.
- Use
awaitto pause until the promise resolves, and remember every Playwright action is async.
Try thisconst title = await page.title()gives you the string. Forget theawaitandtitleis a Promise object, so your assertion compares a promise to a string and fails in a way the error message never explains clearly.Your turnIn the "Async / await" drill, a function getUser() returns a promise that resolves after a short delay. Await it and print the user’s name. Then remove the await and run it again to see what you get instead.
Show the answer
With
await:const user = await getUser(); console.log(user.name)prints the name. Withoutawaityou log a Promise object (orundefinedwhen you try to read.nameoff it). This is the number-one beginner automation bug: a forgottenawait. Seeing[object Promise]orPromise { <pending> }in your output should instantly make you reach forawait.When an assertion fails with a value like
Promise { <pending> }or[object Promise], you forgot anawait. Train yourself to recognise that on sight; it will save you hours.async / awaitPromisesPlaywright- Learn that JSON is just text shaped like objects and arrays, and use
JSON.parseto turn it into data andJSON.stringifyto turn it back. - Make a real request in code with
fetch, then readresponse.statusandawait response.json(). - Pull a field out of the parsed response and assert on it, the same check you did by hand in Postman, now in code.
Try thisconst res = await fetch(url); const data = await res.json();thenconsole.log(res.status, data.id). You have just done in five lines what a Postman request does in a window, and now it can run unattended.Your turnIn the "JSON" drill you are given a JSON string. Parse it, then print the value of the "status" field and the email of the first item in its "users" array.
Show the answer
Parse, then read the fields:
const obj = JSON.parse(raw); console.log(obj.status); console.log(obj.users[0].email). The skill is moving comfortably between text (the raw response on the wire) and data (something you can read fields out of). Every API test you write later is this loop: get text, parse it, assert on a field.A raw API response is text; you cannot read
.emailoff a string.JSON.parseis the bridge. The reverse,JSON.stringify, is how you log an object so you can actually read it.JSON.parsefetchresponse.json()- Learn that JSON is just text shaped like objects and arrays, and use
- Learn the daily git loop: clone, make a branch, add, commit with a clear message, push, then open a pull request.
- Learn pull and how to handle a basic merge conflict without panicking.
- Learn just enough terminal: cd to move, ls to look, and running an npm script like
npm test.
Try thisA normal day:
git checkout -b add-login-tests, write a test,git add .,git commit -m "Add login regression tests",git push, open a PR. That loop is how every test you write reaches the team.You do not need to master Git, you need the loop above and the courage to google the rest. "How do I undo my last commit" is a question every engineer still searches; that is normal.
GitGitHubThe terminalnpm- Read the error from the top: the message and the first line of your own code in the stack are where the truth is.
- Use
console.logto inspect what a value actually is right before the line that fails. - Set a breakpoint in VS Code and step through, so you watch the values change instead of guessing.
Try thisTypeError: Cannot read properties of undefined (reading "email")tells you something you expected to be an object was undefined. Log it one line earlier and you will usually see an empty array or anullyou did not handle.Your turnThe "Debug me" drill has a small program that throws an error when you run it. Read the message, find the line, and fix it so it prints the user’s email.
Show the answer
The bug is reading a field off something that is undefined (an out-of-range array index or a misspelled field). The fix is to read the correct index or guard it:
if (users.length) console.log(users[0].email). The habit that matters more than the fix: you read the error, found the line it pointed to, and changed one thing, instead of randomly editing until it worked. That is debugging.Resist editing randomly until an error disappears. Read the message, read the line it points to, and form one hypothesis before you change anything. Slower looks faster here.
Stack tracesconsole.logVS Code debuggerBreakpoints
Automation
Automate your first test
Now that you can read and write code, this is where it clicks. A real framework, and the machine starts testing for you. About 80% of QA postings now ask for automation.
- Learn the priority order: user-facing roles and labels first, then test IDs, then CSS/XPath as a last resort.
- Practice scoping and chaining so a locator matches exactly one element, not five.
- Ask devs to add stable
data-testidattributes to elements with no accessible name.
Try thisPrefer
page.getByRole("button", { name: "Add to cart" })over.locator("button.btn.btn-primary.add-cart"). The role survives a CSS refactor; the class chain breaks the moment a designer renames a class.Switching from CSS/XPath to role-based locators kills more flaky tests than any other single change. If you cannot target by role, that is often a real accessibility bug.
Playwright InspectorgetByRole / getByTestIdcodegen- Scaffold with
npm init playwright@latest: it generates the config, a sample test and a GitHub Actions workflow. - Run it headless, then with
--uito watch each step execute. - Use
codegento record a flow, then refactor the generated locators into clean role-based ones.
Try thisA real first test:
goto("/login"),getByLabel("Username").fill("demo"),getByRole("button", { name: "Sign in" }).click(), thenexpect(getByRole("heading", { name: "Dashboard" })).toBeVisible().Codegen is a great scaffold but a terrible final product, it loves over-specific locators. Always treat its output as a first draft you clean up immediately.
PlaywrightWebdriverIOcodegennpm- Scaffold with
- Learn auto-waiting: Playwright already waits for visible, enabled and stable before it acts.
- Use assertions that auto-retry until true or timeout, like
expect(locator).toBeVisible(). - Treat any
waitForTimeoutin the codebase as a smell to hunt down and remove.
Try thisReplace
await page.waitForTimeout(3000)withawait expect(page.getByRole("alert")).toHaveText("Saved"). The first wastes 3s on fast runs and still flakes on slow ones; the second waits exactly as long as needed.A hard-coded wait is the number-one cause of slow AND flaky suites at once: too long on a fast machine, too short on a loaded CI runner. Never ship one.
Playwright expectweb-first assertions- Use the request fixture to hit endpoints directly, no browser needed.
- Assert in order: status first, shape second, values third.
- Seed test data via API (create a user, seed a cart) so the UI test starts in a known state.
Try thisconst res = await request.post("/api/users", { data: { name: "Ada" } }); expect(res.status()).toBe(201). Then drive the UI for only the one behaviour you are actually verifying.Modern features sit on a few API calls. Seeding state via API instead of UI clicks makes tests faster and far less flaky.
Playwright requestREST Assuredpytest + httpx- Set
trace: "on-first-retry"so CI failures capture a full trace automatically. - Open the trace to step through DOM snapshots, network and console at the moment of failure.
- Use
--debugand breakpoints locally to edit locators live and watch them highlight.
Try thisA test fails on CI; the trace shows it clicked the button, but the DOM snapshot reveals a cookie banner was covering it, something you would never see from the stack trace alone.
When a test fails, resist adding a retry or a sleep. Open the trace first. Nine times out of ten it shows the real cause in under 30 seconds.
Playwright Trace ViewerUI ModeVS Code extension- Set
- Use the generated workflow, or add
playwright install --with-depsplusplaywright testto a CI job. - Upload the HTML report and traces as artifacts so failures are inspectable without re-running.
- Trigger on
pull_requestso nothing merges without tests passing.
Try thisA minimal job runs
npx playwright install --with-deps chromiumthennpx playwright test, finished with an upload-artifact step pointing at playwright-report/.Install only the browsers you actually run in CI (
install --with-deps chromium). It shaves real minutes off every run, which adds up fast across a team.GitHub Actionsupload-artifactHTML reporter- Use the generated workflow, or add
SDET & engineering
Build suites that last
Anyone can write ten tests. The craft is a suite your team still trusts a year from now: fast, isolated, and honest about what it covers.
- Move locators and actions out of tests into page-object classes, so a UI change is a one-line fix in one place.
- Use custom fixtures to inject logged-in pages and seeded data instead of copy-pasting beforeEach blocks.
- Extract repeated logic (date builders, unique-email generators) into small helper modules.
Try thisA
LoginPageclass exposeslogin(user, pass)so 40 tests callloginPage.login(...). When the form changes, you edit one method, not 40 tests.A page object should expose intent (
addToCart()), not raw locators. If tests still reach in for a selector, the abstraction is leaking and maintenance pain follows.Page Object ModelPlaywright fixturespytest fixtures- Rank candidates by business risk times frequency times cost of failure, and automate the critical path first.
- Automate stable, repetitive, high-value regression; leave exploratory and one-off checks manual.
- Track ROI by defect escape rate and recaptured manual hours, not by raw test count.
Try thisAutomate checkout-and-payment (high risk, every release) before "edit profile avatar" (low risk, rarely changes), even though the avatar test is easier to write.
10,000 low-signal tests have negative ROI, the maintenance outweighs the bugs they catch. Delete tests that never fail meaningfully.
Risk matrixTestRail / XrayCoverage reports- Push coverage down: many fast unit tests, a solid API/integration layer, a thin slice of end-to-end.
- Cap E2E to critical user journeys, they are the slowest and flakiest, so spend them wisely.
- Add static analysis (lint, type-checks) as the cheapest test that runs before anything dynamic.
Try thisInstead of 200 E2E tests for every form-validation permutation, write 5 happy-path journeys and push the other 195 down to fast API/component tests that run in seconds.
In 2026 the pyramid is bending toward a "trophy", with integration tests carrying more weight. Either way: the higher and slower the test, the fewer you should have.
Vitest / JestPlaywrightPactESLint- Define projects for Chromium, Firefox and WebKit so one suite covers all three.
- Enable parallel workers and shard across CI machines with
--shardand a matrix. - Set retries: 2 on CI and 0 locally, so CI noise does not block merges while local stays honest.
Try thisA matrix of shard 1..4 runs
playwright test --shard=${index}/4, cutting a 20-minute suite to about 5 minutes across four runners.Sharding only helps if tests are truly independent. One test that depends on another’s leftover data passes solo and fails randomly once sharded. Fix isolation first.
GitHub Actions matrixPlaywright --shardprojects- Emit a blob report per shard, then combine them into one HTML report.
- Tag known-flaky tests and exclude them from the blocking run so they stop breaking main.
- Give every quarantined test an owner, a ticket and a deadline. Quarantine is a hospital, not a graveyard.
Try thisA test that fails then passes on retry gets badged flaky, ticketed, and pulled from the merge gate, while a dashboard keeps tracking it so it cannot be forgotten.
Retries are a bandage for a test you are actively fixing, never a policy. If retries hide 50 flaky tests, you have a suite that lies to you about quality.
merge-reports / blobCurrentsAllureTrunk- Add visual regression with
toHaveScreenshot(), tuning the threshold to ignore anti-aliasing noise. - Plug in
axe-coreto scan pages for WCAG violations as normal test steps. - Fail the build on new violations, and re-baseline screenshots deliberately by PR, never blindly.
Try thisAn accessibility gate:
const results = await new AxeBuilder({ page }).analyze(); expect(results.violations).toEqual([]). A CSS refactor that pushes Buy off-screen passes functional tests but fails the visual check.Generate screenshot baselines in the same OS/container CI runs on. Fonts render differently on macOS and Linux, so a laptop baseline diff-fails on every CI run.
toHaveScreenshot@axe-core/playwrightApplitools- Add visual regression with
- Make the test job a required status check in branch protection, so a red suite physically blocks merge.
- Define the gate: smoke and critical E2E pass, no new a11y violations, coverage holds, no unquarantined flakes.
- Keep it fast (under about 10 minutes) by running smoke on PR and full regression nightly.
Try thisBranch protection requires the e2e (chromium) check; a PR that breaks checkout cannot be merged even by an admin override. The gate enforces the bar, not a person.
A gate is only as credible as it is fast and stable. The moment people routinely click "merge anyway", the gate is dead. Guard its speed and flake-rate fiercely.
Branch protection@smoke tagsSonarQubeCodecov
AI-augmented QA
Test with AI
You stop hand-typing every test and start directing AI to draft them, while you stay the judge of what is correct. The role shifts from author to quality strategist.
- Install Copilot, Cursor or Claude in your IDE and point it at your existing framework so it matches conventions.
- Prompt it from a bare function or failing test, then run the result immediately. Never commit unrun AI code.
- When it fails, paste the assertion error back and have it fix that specific thing.
Try thisPaste a flaky Selenium test plus its stack trace into Cursor and ask why it fails intermittently. It spots an implicit wait where an explicit WebDriverWait is needed and rewrites it.
Give the AI one real test from your repo and say "match this style". Output quality jumps because it copies your fixtures and assertions instead of inventing its own.
GitHub CopilotCursorClaudePlaywright- Paste the acceptance criteria and ask for a case table: scenario, preconditions, steps, expected result.
- Explicitly request edge cases, negative paths and boundaries. AI defaults to happy paths unless pushed.
- Diff its list against your mental model and add the domain cases it could not know.
Try thisFrom "user can redeem a discount code", a prompt produces ~15 cases including expired, already-used, below-minimum-spend and stacking two codes, the last being a real business rule the writer forgot.
Watch for hallucinated cases: AI will happily test fields and endpoints that do not exist. Every generated case must trace to a real requirement or it gets cut.
ClaudeChatGPTTestCollabKaneAI- Hand a failing test to an AI assistant with everything it needs: the error, the trace summary, the relevant logs. Ask it to localise the cause, not just restate the error.
- Cluster a noisy CI run: have it group failures by likely shared cause so you fix one thing, not fifty symptoms.
- Confirm its diagnosis against the trace yourself. AI proposes the cause; you verify it before you change anything.
Try thisPaste a stack trace plus the Playwright trace summary into Claude and ask "what failed and why". It spots that a cookie banner intercepted the click across twelve tests, so one fix clears the whole batch instead of twelve separate investigations.
AI is fastest at reading what already happened: logs, traces, diffs. Let it run the first-pass triage, then you make the call on what is actually broken and what blocks the release.
ClaudeCursorPlaywright Trace ViewerAllure / Currents- Add Applitools or Percy to one critical flow: checkout, dashboard, signup.
- Capture a baseline, then let Visual AI flag layout shifts, overlaps and responsive breaks across viewports.
- Triage each diff as bug or intended change, accepting new baselines deliberately.
Try thisA CSS refactor pushes the Buy button off-screen on iPhone SE. Functional tests pass (the element exists), but Visual AI flags the layout break that selector tests completely miss.
Visual AI catches a class of bugs your asserts never will, but tune the match level per page or you drown in noise from timestamps and ads.
ApplitoolsPercyChromatic- Treat every AI artifact as a proposal, never a commit: accept, edit or reject each one.
- Read each generated test against the real product: does it assert the right behaviour or just a plausible one?
- Keep humans owning the judgement work: exploratory testing, acceptance, and what blocks a release.
Try thisA copilot proposes 40 cases from a spec; the engineer accepts 28, edits 8 with wrong expected results, and rejects 4 that test non-existent fields. Nothing enters the library unreviewed.
A generated test that passes against a buggy expectation is worse than no test. Your review job is to catch the test that confidently asserts the wrong thing.
Copilot (suggest mode)TestCollabJira- Build a reusable prompt library: a case generator, a "manual steps to Playwright" converter, a "find missing edge cases" prompt.
- Always give role, context, constraints and output format.
- Iterate. Your second prompt ("now add auth-failure and rate-limit cases") matters more than the first.
Try thisSave a template that takes any OpenAPI spec and outputs a pytest collection of boundary and auth tests, then reuse it across every new microservice.
Pin a real artifact (the actual spec, a sample test, the error log) into the prompt. Context beats clever wording every time.
ClaudeChatGPTPostmanpytest- Map where AI is weak: novel features, ambiguous requirements, and business-risk judgement.
- Add a review gate so no AI-generated test reaches main without a human approving the expected result.
- Audit the AI-generated suite for tests that mutate state wrongly or validate hallucinated behaviour.
Try thisA team finds an AI-written test that calls
DELETE /users/{id}in setup and corrupts shared data, then adds a lint rule blocking destructive calls in generated tests without approval.AI accelerates coverage, but risk judgement stays human. Deciding which failing test actually blocks a release is a business call, not a model output.
ClaudeCopilotSonarQube / linters- Locate your team on a maturity model: AI assists, AI generates and humans refine, AI runs and humans handle exceptions.
- Start where ROI is provable, AI failure-triage and flaky-test reduction, before buying a full autonomous stack.
- Measure before and after: maintenance hours saved, escaped-defect rate, regression cycle time.
Try thisA lead reports "AI failure-triage cut investigation time from 6 hours a sprint to under 1, and AI generation covered 30% more edge cases", concrete numbers that justify the budget and feed a brag doc.
Most teams sit at the lower maturity levels. Being the person who moves your team one level up, with metrics, is a promotion-grade contribution.
ClaudeAllure / TestRailCurrents
Leadership
Multiply your impact
Senior QA is less about finding bugs and more about people, process and influence. Your leverage now comes from what your team and stakeholders do because of you.
- Cover scope and objectives, levels and types, tools and environments, roles, risk and reporting.
- Anchor it in risk: prioritise by business impact and failure likelihood, not "test everything equally".
- Circulate a draft to dev, product and ops for sign-off. A strategy nobody agreed to is just a document.
Try thisA one-page strategy says payments and auth are critical (full regression plus exploratory each release) while marketing pages get smoke only, and stakeholders sign off and stop demanding 100% coverage everywhere.
Keep it living and short. A one-to-two page strategy people actually read beats a 40-page document that rots in a wiki. Revisit it each quarter.
ConfluenceNotionMiro- Choose the model: QA embedded in each squad, a central team, or a hybrid enablement function.
- Define who owns the automation framework, who owns exploratory, who owns release sign-off.
- Own the unglamorous parts: hiring, resourcing, and budget for tools and headcount.
Try thisA manager moves from a siloed QA gatekeeper to one SDET embedded per squad plus a shared platform team owning the CI infrastructure, cutting handoffs and making quality everyone’s job.
The structure that fits a 5-person startup breaks at 50 engineers. Re-evaluate team shape whenever headcount roughly doubles.
Jira / LinearOrg chartsGitHub CODEOWNERS- Translate findings into consequences: not "37 open bugs" but "checkout has a payment-failure risk affecting 8% of transactions".
- Frame releases as trade-offs: confidence, residual risk, and what is being deferred.
- Replace "QA cannot sign off" with a risk narrative that hands the decision to the business owner.
Try thisInstead of blocking a release, present a go/no-go dashboard: critical paths green, two medium risks deferred to a hotfix, ~5% residual risk on the new coupon flow, your call. Leadership ships with eyes open.
Stakeholders do not fear bugs, they fear surprises. Quantified, business-framed risk turns QA from a blocker into a trusted decision partner.
TestRail / AllureLooker / Power BIConfluence- Be honest about what energises you: deep technical systems point to IC/architect; growing people points to management.
- Research the comp reality; senior IC and architect pay often matches or beats mid-level management.
- Tell your manager your intended track so they sponsor the right opportunities.
Try thisA senior SDET turns down a manager offer, stays IC, and becomes Test Architect owning the cross-team automation platform, keeping the higher comp and the hands-on work they love.
The fork is not permanent, but each switch costs a year or two of momentum. The cleanest career capital is choosing deliberately, not drifting into management because it was the next box.
Career ladderslevels.fyi1:1s- Take one junior, meet regularly, pair on a real bug and review their automation PRs.
- Delegate stretch work (let them own a test plan) instead of doing it for them, then coach the gaps.
- Give specific, timely feedback, both technical and behavioural.
Try thisA lead pairs weekly with a manual tester moving into automation, co-writes their first Playwright suite, then hands them ownership of the regression pack within a quarter.
Mentoring is the single clearest evidence of "leadership" on a promotion case. Track who you grew.
GitHub code review1:1 notesPairing tools- Start internal: a lunch-and-learn, a wiki post on a tricky bug, a recorded walkthrough of your framework.
- Climb the speaking ladder: meetup, then regional, then national, starting with a 99-second or lightning talk.
- Turn solved problems into content: a post per non-obvious bug, or a short tool demo.
Try thisA tester writes "How we cut flaky tests by 60%", posts it on the company blog and dev.to, turns it into a 5-minute meetup talk, and gets invited to a TestBash.
Teaching forces clarity and makes your expertise visible beyond your manager, and visibility is what converts good work into promotions and offers.
dev.to / blogMinistry of TestingYouTube / Loom- Pick one cross-team quality pain, like no shared bug-severity definition, and propose a standard.
- Build a coalition: get dev and product to co-own the change rather than mandating it from QA.
- Pilot small, measure, then scale. Lead by demonstrated results, not authority.
Try thisA lead introduces a company-wide Definition of Done that includes coverage and a risk note, pilots it in two squads, shows fewer escaped defects, and gets it adopted org-wide.
Influence without authority is the senior-leadership skill. The win is when other teams adopt your idea because it works, not because you told them to.
Confluence (RFCs)SlackJira workflows- Pick metrics that reflect real risk: escaped defects, time to detect, critical-path coverage, not vanity counts.
- Build a recurring one-screen report leadership actually reads: trend lines and one insight.
- Tie every metric to a business outcome: fewer incidents, faster safe releases.
Try thisA monthly one-pager: production incidents down 40% quarter on quarter, release cycle cut from 2 weeks to 5 days, critical-path coverage at 95%, positioning QA as a driver of outcomes.
Measure outcomes (incidents prevented, time saved), not activity (tests run). Outcome metrics earn QA a seat at the planning table.
Grafana / LookerTestRailJira dashboards
Make it stick
Drill the vocabulary
The words that come up in every standup and every interview. Flip a card, answer it out loud, then mark whether you knew it. The ones you miss stay in the deck.
0 of 18 known on this pass. The ones you marked "review" are still in the deck.
Stop reading, start running
Run the code yourself
These are the stage 3 drills. Pick one, change the code, hit Run, and read the output. It runs real JavaScript right here in your browser. That type-run-read loop is how programming actually gets into your hands.
Run the code to see output here.Branch out
Eight ways to specialize
The roadmap gets you the core skills. These are the directions you can go deeper in afterward, and for each one, how to actually start, not just what to learn. Most testers pick one or two over a career.
Automation & SDET
Build the test frameworks and tooling your whole team runs on.
- Go past "tests pass" into framework design: page objects, fixtures, custom reporters, and a folder structure another engineer can navigate cold.
- Learn the CI side deeply: sharding, parallelism, flake control, and making runs fast enough that nobody dreads them.
- Read other people’s frameworks on GitHub and copy what is good. Contribute a fix back to Playwright or WebdriverIO.
First build: Build a small open-source test framework against a public demo app, with CI, reporting and a README, and put it on your GitHub.
Performance engineering
Make sure the product stays fast and stable under real load.
- Learn to read a system, not just a tool: percentiles (p95/p99 over averages), throughput, latency, and where the bottleneck actually is.
- Build one realistic load scenario in JMeter, the long-time standard, then correlate the results with server metrics in Grafana. Reach for k6 when your team wants tests-as-code in the pipeline.
- Pair with whoever owns infrastructure; performance work lives at the seam between testing and operations.
First build: Record a realistic user journey in JMeter, ramp the threads, and chart the point where response times start to climb.
Security
Find and close vulnerabilities before they reach production.
- Start with the OWASP Top 10 and learn to actually exploit each one in a safe lab, not just name it.
- Practise on deliberately vulnerable apps (OWASP Juice Shop, PortSwigger’s Web Security Academy, which is free and excellent).
- Bring security into your normal testing: every "valid token, wrong owner" check from stage 2 is the entry point to this track.
First build: Work through the PortSwigger Web Security Academy labs on broken access control and injection, then write up one finding as a bug report.
Mobile QA
Keep native iOS and Android apps solid across real devices.
- Get fluent in the mobile-only failure modes from stage 2: gestures, interruptions, deep links, permissions, and low-end devices.
- Automate with Appium for cross-platform, or go native with Espresso (Android) and XCUITest (iOS) for speed and stability.
- Learn a device farm (BrowserStack, Sauce Labs) so your suite runs across real hardware, not just one simulator.
First build: Automate a login-and-core-action flow on a sample app with Appium, then run it on two real devices via a free device-farm trial.
Accessibility
Make the product work well for people of every ability.
- Go beyond automated scans: learn to navigate with a keyboard only and with a screen reader (VoiceOver, NVDA), because that is where the real issues are.
- Learn WCAG 2.2 well enough to map a finding to a specific success criterion in your bug reports.
- Consider the CPACC certification once you can already do the work; it signals you genuinely know the standard.
First build: Run a full keyboard-and-screen-reader audit of one real product flow and write it up against WCAG criteria.
AI quality
Test AI features, and put AI to work testing everything else.
- Split the track in two: testing AI features (non-determinism, bias, hallucination, drift) and using AI to test (generation, failure triage, visual checks).
- Learn to test things that do not give the same answer twice: evals, golden datasets, tolerance ranges instead of exact matches.
- Build the prompt-engineering and rules-file skills from stage 6 into a repeatable, shareable toolkit.
First build: Write an eval harness that scores an AI feature against a small golden dataset, and flag where it drifts or hallucinates.
Quality engineering
Bake quality into the CI/CD pipeline and the cloud from the start.
- Move quality left and right: into the pipeline (gates, fast feedback) and into production (monitoring, synthetic checks, observability).
- Learn Docker and the basics of a cloud platform so your tests and environments are reproducible and disposable.
- A cloud certification (AWS) pays off here, where tests live inside cloud pipelines and infrastructure.
First build: Containerise a test suite with Docker and wire it into a CI pipeline that spins up its own environment, runs, and tears down.
QA leadership
Set the quality strategy and grow the testers around you.
- Practise the stage 7 skills early: write a real test strategy, mentor one person, and translate findings into business risk.
- Learn to measure outcomes (escaped defects, time to detect) and report them in one screen leadership will actually read.
- Build influence without authority: get other teams to adopt your idea because it works, not because of your title.
First build: Volunteer to own the test strategy for one project end to end, get it signed off by dev and product, and report on it monthly.
Grow on purpose
The career moves no one teaches you
Skills get you in the door. These habits are what actually move you up: making your work visible, staying curious, and building in public.
Keep a brag document
Every Friday, jot what you shipped, fixed, reviewed or unblocked. At review time it lets your manager make a specific case for promotion instead of "they do great work".
Ask with evidence, not vibes
Bring the brag doc and frame it as impact: "I cut escaped defects 40% and halved regression time, that is senior scope." Quantified impact is hard to wave off.
Make your work visible
Demo wins in sprint review, post a short writeup when you solve something gnarly, and CC the right people. Great work nobody knows about does not get promoted.
Build in public
Keep a GitHub with a real framework, a blog with one post per interesting bug, and a talk or two. It is a live, searchable resume that recruiters actually check.
Curiosity is your edge
The instinct to ask "what happens if..." and probe edge cases is exactly what AI cannot replicate, because it only knows patterns it has seen. Lean into it.
Have a system for getting unstuck
Time-box solo struggle to about 30 minutes, then escalate: re-read the error aloud, rubber-duck it to an AI with the full trace, search the forum, then ask a human with "here is what I tried".
Speak business and risk early
Practice turning "we found 12 bugs" into "here is the risk to checkout and what I would do". It is the skill that pulls people into strategy instead of the bug queue.
Find your people (and keep showing up)
Your next job and best ideas usually come through people, not job boards. Pick one community below, introduce yourself, answer one question a week, and within months you have a network instead of a resume.
Find your people
Communities worth your time
Nobody does this alone. These are the rooms where testers swap war stories, post jobs, and pull each other up. Start with one, introduce yourself, and keep showing up.
The big tent of software testing: The Club forum, a busy Slack, Dojo courses and TestBash conferences. The single best first stop.
VisitFree, structured courses on Playwright, Selenium, API testing and the programming foundations from stage 3. (I teach the WebdriverIO one.)
VisitA large, welcoming testing community with meetups, a Discord, conferences and challenges. Especially strong if you are in or near Asia.
VisitWhere people ask the unfiltered career questions: salaries, job-hunting, "is this normal at my company". Lurk first, then ask.
VisitFor going deep on the craft: context-driven testing, Rapid Software Testing ideas, and a community that takes the thinking seriously.
VisitSearch Meetup for a testing, QA or automation group near you. Nothing builds a network faster than a room of people doing your job in your city.
VisitTestBash, STAREAST/STARWEST, SauceCon, Automation Guild. Watch the talks online first, then aim to attend, then aim to speak. I have spoken at several.
VisitFollow testers whose work you admire, post what you learn, and reply thoughtfully. It is where a lot of QA hiring and opportunity actually flows now.
VisitAn honest take
Certifications, ranked by ROI
You don't need a wall of badges. Here's which ones are worth your time in 2026, and which to skip until a job actually asks for them.
Near table-stakes in Europe, India and regulated or consulting work. Lighter ROI at US product companies, who weigh a portfolio higher.
Fast to earn and immediately useful day to day. The AI testing cert most testers should reach for first.
Real credibility if you want to specialize in accessibility. Signals you actually know WCAG.
Pays off on the quality-engineering and DevOps track, where tests live in cloud pipelines.
Worth it if you test AI products or work in regulated AI. Heavier than CT-GenAI and overkill for general QA.
A public GitHub with real, passing tests beats a Selenium or vendor badge almost everywhere that matters.
Straight answers
Questions everyone asks
The honest version of what people message me about breaking into QA and growing once they're in.
Do I need a computer science degree?
No. Plenty of strong testers come from support, teaching, science and finance, anywhere curious and careful people are made. Demonstrated testing ability and clear communication beat a diploma. A portfolio or a cert can stand in for the degree.
I am not a "coder". Can I really learn enough to automate?
Yes, and you need far less than you think. Stage 3 of this roadmap is the whole list: variables and types, arrays and objects, loops and functions, async/await, JSON, and reading an error. That is enough to write and fix real tests. Learn it by running code, not just reading, which is exactly why the playground on this page exists.
Is manual testing dead?
No, but rote, click-the-same-thing manual testing is fading. Exploratory testing, usability and judgement are more valuable than ever. The best testers in 2026 do both: they automate the boring paths and use their brain on the rest.
Will AI replace QA?
It is replacing tedious execution, not judgement. AI drafts tests, heals locators and triages failures; humans own strategy, edge cases and deciding what 'good enough' means. The market is shifting toward testers who can think, not disappearing.
How long until I can get a job?
It depends far more on consistency than on any fixed timeline, and AI tools are shortening the ramp every year. Show up regularly, build real things, and put your work where people can see it. Steady practice gets you there faster than any calendar promise.
Do I have to learn to code?
Not for an entry manual or analyst role. But coding, whether JavaScript, TypeScript or Python, is the gate to automation, SDET and the more senior roles. Learn just enough to be useful, then keep going. Stage 3 is exactly that "just enough".
Which automation tool should I learn first?
Playwright. It overtook the field in 2025 and 2026 on adoption, satisfaction and job postings. Selenium still has the most listings overall and the widest language support, so it is a fine second. WebdriverIO is great paired with mobile.
Are remote QA jobs still a thing?
Yes. QA remains one of the more remote-friendly tech roles, with thousands of remote listings and healthy demand. Automation and SDET skills widen the pool considerably.
Should I get ISTQB certified?
It depends where you are. In Europe, India and regulated or consulting work it opens doors. At US product companies a strong portfolio usually does more. Start with the work, then add the cert if your target market asks for it.
Brand new to testing?
Start with the free Become a QA Engineer course
The roadmap shows you the whole journey. If you're at the very beginning, this structured, free course is how you take the first stage: foundations, test design, bug reporting and your first automation.
Stuck on a stage?
Everyone is at stage one once. Reach out on LinkedIn, or send me a message and tell me where you got stuck.
Field Notes
Build better products
by testing what matters.
Get practical notes on testing, automation, AI, mobile apps, and release decisions. I share the workflows, lessons, tools, and mistakes from real product work so you can ship with more confidence and fewer last-minute surprises.
