QA and test automation cheat sheet
Testing on a few screens: test design techniques, what to test at which layer, a bug report template, Playwright, Jest and Vitest commands, API and accessibility checks, fixes for flaky tests, load testing and UAT sign-off. Keep it open while you plan tests or write them.
Test design techniques
Each technique finds a different kind of bug with fewer test cases than guessing. The examples use Mangrove's app and Store app (Test design).
| Technique | Use it when | Mangrove example | Lesson |
|---|---|---|---|
| Equivalence partitioning | Many inputs behave the same way | Order quantity: one test for 1 to 99 (valid), one for 0 or less, one for 100 or more | Test design |
| Boundary values | A rule has a limit | Max 99 per item: test 0, 1, 98, 99, 100; free delivery from $20.00: test $19.99 and $20.00 | Test design |
| Decision table | Several conditions combine into one outcome | Discount = member? × coupon? × promo item? with the expected price for each of the 8 rows | Test design |
| State transition | Something moves through states | Order: placed, packed, out for delivery, delivered, cancelled; test every allowed move and a few forbidden ones | Test design |
| Pairwise | Too many combinations of settings | Android version × screen size × network × language: cover every pair, not every combination | Test design |
| Error guessing | You know where bugs usually hide | Empty cart, emoji in names, double tap on "Pay", app killed mid-checkout | Exploration |
| Exploratory session | New feature, unclear risks | Charter: "Explore receiving a delivery with a damaged pallet, to find where stock counts go wrong", 60 minutes, notes | Exploration |
| Risk-based selection | Not enough time to test everything | Test payment and stock first, profile pictures last; likelihood × impact | Why we test, Test strategy |
What to test at which layer
Most checks belong low in the pyramid, where they are fast and pin the bug to one place. Keep end-to-end tests for the few journeys that must never break (Test strategy).
| Layer | Tests | Rough count and speed | Tools |
|---|---|---|---|
| Static checks | Types, lint rules, formatting | Whole repo in seconds | TypeScript, ESLint, Prettier |
| Unit | One function or component: price rounding, VAT, discount rules | Hundreds to thousands, milliseconds each | Jest, Vitest, React Testing Library |
| Integration and API | An endpoint with a real database; auth, validation, status codes | Tens to hundreds, under a second each | Supertest, Playwright request, a test Postgres |
| Contract | The app and the API agree on request and response shapes | One set per consumer-provider pair | Pact, OpenAPI schema checks |
| End-to-end | Browse, add to cart, check out; receive a delivery | 5 to 30 journeys, seconds each | Playwright, Detox or Maestro for mobile |
| Exploratory and UAT | What scripts miss; whether it fits the real job | Time-boxed sessions | Charters, a test store such as Ama's |
Bug report template
One bug per report. A developer should be able to reproduce it without asking you anything (Bugs and UAT).
Title: Checkout total ignores coupon when cart has a promo item (Android)
Environment: Mangrove app 4.12.0 (build 812), Android 14, Pixel 7, staging API
Severity: Major Priority: High Found by: exploratory session, checkout charter
Steps to reproduce
1. Sign in as test user qa.member@example.test (member)
2. Add "Basmati rice 5 kg" (promo item) and "Milk 1 L" to the cart
3. Apply coupon SAVE10
4. Open the checkout screen
Expected: discount line -$0.12 (10% of Milk 1 L at $1.20); total $16.38
Actual: total $16.50; coupon shows "applied" but no discount line
Frequency: 5 of 5 tries. Does not happen without the promo item.
Evidence: screen recording, request id 7f3c2a in API logs
Notes: iOS 17 on the same build shows the right total| Field | Means | Who sets it | Example |
|---|---|---|---|
| Severity | How bad the effect is | Tester | Critical: tills cannot take payment. Minor: a typo on a receipt |
| Priority | How soon to fix | Product owner with the team | A typo in the festival-sale banner can be minor severity but top priority |
| Status | Where it is | Whoever moves it | New, triaged, in progress, ready to test, closed, won't fix |
Playwright: "I want to..."
Commands for @playwright/test run from the project folder (Playwright E2E).
| I want to... | Command | Lesson |
|---|---|---|
| Add Playwright to a project | npm init playwright@latest | Playwright E2E |
| Run every test | npx playwright test | Playwright E2E |
| Run one file, or tests whose title matches | npx playwright test tests/checkout.spec.ts, -g "applies coupon" | Playwright E2E |
| Run in one browser only | --project=chromium | Playwright E2E |
| Watch the browser | --headed; step through with --debug | Playwright E2E |
| Use the interactive runner | npx playwright test --ui | Playwright E2E |
| Record a test by clicking | npx playwright codegen https://staging.example.test | Playwright E2E |
| See what happened in a failed run | --trace on, then npx playwright show-trace trace.zip | Stable CI |
| Open the HTML report | npx playwright show-report | Playwright E2E |
| Hunt a flaky test | --repeat-each=20 --workers=1 on that test | Stable CI |
| Update screenshot baselines | --update-snapshots (short: -u) | Playwright E2E |
| List tests without running them | --list | Playwright E2E |
Locators and assertions
Find elements the way a user does. Role and label locators survive redesigns; CSS paths tied to layout break every sprint. Web-first assertions such as toBeVisible retry until a timeout, so you never need a fixed sleep (Playwright E2E, Accessibility).
import { test, expect } from '@playwright/test';
test('member coupon lowers the total', async ({ page }) => {
await page.goto('/cart');
await page.getByLabel('Coupon code').fill('SAVE10');
await page.getByRole('button', { name: 'Apply' }).click();
await expect(page.getByRole('status')).toHaveText('Coupon applied');
await expect(page.getByTestId('cart-total')).toHaveText('$14.85');
});| Prefer, in this order | Example | Avoid |
|---|---|---|
| Role and accessible name | getByRole('button', { name: 'Place order' }) | page.locator('div.btn-primary:nth-child(3)') |
| Form label | getByLabel('Email') | Placeholder text that changes with copy edits |
| Visible text | getByText('Out of stock') | Long XPath expressions |
| Test id, when nothing else is stable | getByTestId('cart-total') with data-testid in the markup | Generated class names |
| Auto-retrying assertions | toBeVisible(), toHaveText(), toHaveURL(), toHaveCount() | page.waitForTimeout(3000) |
Jest and Vitest side by side
The APIs are nearly the same; Vitest uses vi where Jest uses jest. Vitest watches by default in a terminal and runs once in CI (Unit and API tests, Testing React).
| I want to... | Jest | Vitest | Lesson |
|---|---|---|---|
| Run once | npx jest | npx vitest run | Unit and API tests |
| Watch while I code | npx jest --watch | npx vitest | Unit and API tests |
| Run tests whose name matches | npx jest -t "rounds VAT" | npx vitest run -t "rounds VAT" | Unit and API tests |
| Measure coverage | npx jest --coverage | npx vitest run --coverage (needs @vitest/coverage-v8) | Test strategy |
| Fake a function | jest.fn() | vi.fn() | Unit and API tests |
| Replace a module | jest.mock('./payments') | vi.mock('./payments') | Unit and API tests |
| Control time | jest.useFakeTimers() | vi.useFakeTimers() | Debug and test |
| Compare objects, not identity | toEqual for values, toBe for primitives and same object; await expect(p).rejects.toThrow() for promises | Debug and test | |
API testing checklist
Run each row against every new endpoint. curl -i shows the status line and headers; add -H 'Content-Type: application/json' -d '{"qty":2}' to send a body (API testing).
| Check | Example on POST /stores/S-014/orders | Expect | Lesson |
|---|---|---|---|
| Happy path | Valid body and token | 201, the new id, a response that matches the schema | API testing |
| No or bad token | Drop the Authorization header | 401 | API testing |
| Someone else's resource | A token for store S-102 | 403 or 404, never the data | Access control |
| Invalid input | Missing field, qty -1, qty "two", a 10 MB body | 400 or 422 with a clear message; nothing saved | API testing |
| Retry the same request | Same Idempotency-Key twice | One order, the same response both times | Unit and API tests |
| Conflicts | Two requests for the last item in stock | One succeeds, one gets 409 | API testing |
| Lists | Page past the end; a filter with no results | Empty list, not an error; stable order | API testing |
| Limits | Fire 200 requests in a minute | 429 with Retry-After | API testing |
| Contract | The app's expected shape against the provider | Pact or schema check passes in CI | Contract tests |
Flaky tests: if you see X, reach for Y
A flaky test is a bug in the test, the app or the environment. Quarantine it with an owner and a date; do not just add retries and forget it (Stable CI).
| You see | Likely cause | Fix | Lesson |
|---|---|---|---|
| Passes locally, fails in CI | Slower machine; different time zone, locale or screen size | Web-first assertions; set timezoneId, locale and viewport in config | Stable CI |
| Fails only when run with other tests | Shared data or order dependence | Each test creates its own user and data; no shared state | Stable CI |
| "Element not found" now and then | Clicking before the page is ready; brittle selector | Role or label locators; wait for a visible state, not a sleep | Playwright E2E |
| Fails around midnight or month-end | Real clock in the test | Fake timers or a fixed clock (page.clock in recent Playwright) | Debug and test |
| Fails when a third-party service is slow | Real network calls | Mock at the boundary (page.route, MSW); test the real service in a separate contract or smoke test | Contract tests |
| Screenshot diff on every run | Animations, fonts, dynamic dates | Disable animations, mask dynamic areas, run in one fixed container image | Stable CI |
Accessibility quick checks
Automated scanners such as axe (@axe-core/playwright) catch only part of the problems. The keyboard and a screen reader catch most of the rest. Targets are WCAG 2.2 level AA (Accessibility, Accessible and fast).
| Check | How | Pass when | Lesson |
|---|---|---|---|
| Keyboard only | Tab, Shift+Tab, Enter, Space, arrow keys, Esc | Every action works, in a sensible order, with no trap | Accessibility |
| Visible focus | Tab through the page | You can always see where focus is | Accessibility |
| Text contrast | DevTools or a contrast checker | 4.5:1 for normal text; 3:1 for large text and for icons and borders that carry meaning | Accessibility |
| Names and labels | Screen reader or the accessibility tree in DevTools | Every control and input says what it is; images have useful alt text or alt="" if decorative | Accessibility |
| Zoom and reflow | Zoom to 200%, then a 320 px wide window | Nothing is cut off; no sideways scrolling for text | Accessibility |
| Target size | Measure tap targets | At least 24 by 24 CSS pixels (WCAG 2.2); 44 or 48 is kinder on phones and scanners | Accessibility |
| Errors | Submit a form wrongly | The error is in text, next to the field, and announced | Forms |
| Screen reader pass | NVDA on Windows, VoiceOver on macOS and iOS, TalkBack on Android | The main journey can be completed by ear | Accessibility, Test and release |
Load testing in one screen
Test against staging sized like production, never against production without an agreed window. Decide the pass mark before you run: for example p95 under 500 ms and under 1% errors at 4 times normal traffic, Mangrove's festival peak (Load testing).
// k6 script: run with k6 run browse.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 50,
duration: '5m',
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.get('https://staging.example.test/api/products?store=S-014');
check(res, { 'status is 200': (r) => r.status === 200 });
sleep(1);
}| Test type | Shape | Answers | Lesson |
|---|---|---|---|
| Smoke | A few users for a minute | Does the script and the system work at all? | Load testing |
| Load | Expected peak for 15 to 60 minutes | Do we meet the targets on a busy day? | Load testing |
| Stress | Keep raising load past the peak | Where does it break, and how: slowly or all at once? | Load testing |
| Spike | Jump from normal to peak in seconds | What happens when a push notification goes to 2 million customers? | Load testing |
| Soak | Normal load for hours | Memory leaks, filling disks, connection pools that never give back | Load testing |
UAT and release sign-off
User acceptance testing asks whether the change fits the real job, done by the people who do it, such as Ama's store team on real handheld scanners. Agree entry and exit criteria before it starts (Bugs and UAT, Change and UAT).
- Entry: build deployed to the UAT environment, test data loaded, known issues listed, testers briefed with scenarios written in business words.
- During: testers log issues in one place; QA triages daily with the product owner.
- Exit: every scenario run; no open critical or major bugs; minor ones accepted in writing with a fix date.
- Sign-off note: build number, scenarios passed, open issues accepted, who signed and when, and the rollback plan.
Glossary
Plain definitions of the testing terms used across the QA courses.
- Acceptance criteria
- The conditions a story must meet to be accepted, often written as Given, When, Then. Stories and criteria
- Boundary value
- A value at the edge of a rule, such as the maximum quantity, where off-by-one bugs live. Test design
- Charter
- A one-line mission for an exploratory session: explore a target, with a resource, to find a kind of risk. Exploration
- Code coverage
- The share of code lines or branches that tests ran; it shows what is untested, not that tested code is right. Test strategy
- Contract test
- A test that checks a consumer and a provider agree on the shape of requests and responses. Contract tests
- End-to-end (E2E) test
- A test that drives the whole system through the user interface, as a user would. Playwright E2E
- Exploratory testing
- Designing and running tests at the same time, learning as you go, inside a time box. Exploration
- Fixture
- Setup a test needs, such as a signed-in page or a seeded store, created fresh for each test. Playwright E2E
- Flaky test
- A test that sometimes passes and sometimes fails on the same code. Stable CI
- Mock, stub, fake
- Stand-ins for real dependencies: a stub returns fixed answers, a mock also checks how it was called, a fake is a simple working version. Unit and API tests
- Regression
- Something that used to work and broke after a change. Why we test
- Severity and priority
- Severity is how bad the bug's effect is; priority is how soon it should be fixed. Bugs and UAT
- Smoke test
- A few quick checks that the most important things work, run right after a deploy. Test strategy
- Test oracle
- How you know the right answer: a spec, a calculation, an older version or a person's judgement. Test design
- Test pyramid
- Many fast low-level tests, fewer integration tests and only a few end-to-end tests. Test strategy
- Trace
- Playwright's recording of a run: actions, DOM snapshots, network and console, for debugging failures. Stable CI
- UAT
- User acceptance testing: the people who will use the change confirm it fits their work. Bugs and UAT
- WCAG
- Web Content Accessibility Guidelines; level AA of version 2.2 is the usual target. Accessibility
- Web-first assertion
- An assertion that retries until the page reaches the expected state or a timeout passes. Playwright E2E
- p95 latency
- The response time that 95 out of 100 requests are faster than; the usual load-test target. Load testing
Courses on Kolom
- Software Testing and QA: test design, exploratory testing, API and accessibility testing, bug reports and UAT.
- Test Automation and Quality Engineering: strategy, unit and API tests, Playwright, contract and load tests, and a stable CI.
- React for the Web: ends with testing React components the way users use them.
- Mobile Apps with React Native: testing and releasing a mobile app to the stores.
What to read next
- Playwright documentation at playwright.dev. The guides on locators, assertions and the trace viewer are short and exact.
- The Practical Test Pyramid by Ham Vocke, at martinfowler.com. What to test at each layer, with examples.
- WCAG 2.2 quick reference at w3.org/WAI. Every success criterion with techniques, filterable by level.
- Grafana k6 documentation at grafana.com/docs/k6. Test types, thresholds and scenarios.
- Explore It! by Elisabeth Hendrickson. The best short book on charters and exploratory testing.