Free course · Beginner
Software Testing — Manual and Automation
What you will learn
- Write a bug report a developer can reproduce without asking a single question
- Turn a requirement into test scenarios, boundary values and negative cases
- Run smoke, regression and exploratory testing on purpose rather than by habit
- Write and run a Selenium script in Python against a real web page
- Handle waits and selectors so a suite passes twice in a row, not once by luck
- Publish a small test suite with a report you can show in an interview
Course curriculum
5 modules · 15 lessons · a worked example and a practice task in every lesson
Module 1What testing actually is1 of 53 lessons
Weeks 1–2 — the thinking, before the tools. Most fresher testers are hired for this and nothing else.
A bug report a developer can act on
A defect report is a message to somebody who cannot see your screen: steps, expected result, actual result, environment, evidence. Missing any one of those five costs a round trip.
The lesson
A bug report is a short document that lets somebody who was not there reproduce the problem. Title, environment, steps, expected, actual, and the evidence — a screenshot, a log line or a screen recording. Six lines, and a developer can act without asking you a single question.
The title carries the finding: "Applying to a closed job saves the application and shows a success message" tells the developer where to look. Number the steps so they can be repeated exactly, and say which browser, device and build — "not working on my phone" is not a report.
The mistake is "login not working", which forces a round trip of questions and makes the report look careless. The second is combining three unrelated problems in one report, so two get fixed and the third is quietly closed with them. One finding per report.
Title: Login fails with a VALID password on iPhone Safari Build: web 1.4.2 . iPhone 13 . Safari 17 Steps: 1. Open /login 2. Enter a@b.com / correct-password 3. Tap "Sign in" Expected: lands on the dashboard Actual: shows "Invalid credentials"; the account was created today Attach: screenshot + the console error Reproducible? Yes, 3 of 3 times. That single line is what gets it fixed.Example "Login is broken" is not a report. "On Chrome 120, submitting the login form with a valid new user shows the dashboard and then returns to login after two seconds — expected: stays on dashboard. Recording attached" is a report, and the recorder is how you got the two seconds.
Practice Write five bug reports from a website you use daily, each in the five-part format, and rank them by severity.
The test case worth writing
A test case is a condition, an input and an expected outcome that somebody else can execute identically. If two testers get different results from your case, it is not a test case yet.
The lesson
A test case is one check with an expected result: an id, a short title, the precondition, the steps, the expected outcome and the actual outcome once you run it. It exists so somebody else can run the same check next month and get the same verdict.
Write the ones that carry risk first — money, login, data loss, anything a customer would call about — and keep the coverage visible in a sheet or a tool so nobody has to guess what has been checked. A test case that cannot fail is not a test case: "verify the page loads" passes even when the page is wrong.
The mistakes are vague expectations ("should work properly") and steps that depend on your own data ("login as the user I created"). Name the account, the record and the value, or the case is not repeatable and will be run once and abandoned.
id,title,steps,data,expected,priority TC-01,Login valid,open /login; enter email; enter pwd; click sign in,valid user,dashboard shown,High TC-02,Login wrong pwd,same steps,bad pwd,error message shown,High TC-03,Empty email,leave email blank,blank email,validation error,Medium TC-04,Password 7 chars,enter a 7-char pwd,short pwd,validation error,MediumExample For an age field accepting 18 to 60: 17 must be rejected, 18 accepted, 60 accepted, 61 rejected, empty rejected, "abc" rejected. Six cases, written before any tool is opened — that list IS the skill.
Practice Write a full test-case sheet for a signup form: at least fifteen cases including boundaries and one that would only fail if somebody used the wrong operator.
Severity, priority and the words that get read
Severity is how much damage the defect does; priority is how soon it should be fixed. A typo in the company name is low severity and high priority; a crash in an admin-only report is the other way round.
The lesson
Severity is how badly the product breaks; priority is how soon it should be fixed. They are not the same thing and interviewers ask about exactly that. A spelling mistake on the homepage is low severity and high priority; a crash on a screen nobody reaches can be high severity and low priority.
Use the words a tester is expected to use, correctly: blocker (nothing else can be tested), critical (data loss, or money wrong), major (a feature does not work), minor (cosmetic), and regression (something that worked before and does not now). Logging a regression is worth more than logging ten cosmetic issues, because it changes what the team does next.
The mistake is calling everything critical, which trains the team to ignore you. Be specific about the impact in the report — "the candidate cannot apply again because the job now shows as already closed" — and let the numbers decide the severity.
Severity = how bad it is. Priority = how soon it must be fixed. High severity, low priority: crashes on a rare legacy browser. Low severity, high priority: the logo is wrong on the homepage today. Say both in one line so a triage call is instant: "S2 / P1 - checkout total shows Rs.0 for some carts."Example Getting this wrong is what makes a tester look junior: escalating a cosmetic issue as Critical burns the credibility you need for the real one next week.
Practice Classify your five bug reports on both axes, then write the one-line justification you would give a project manager for each.
Module 2Manual testing, end to end2 of 53 lessons
Weeks 3–4 — requirements, techniques and the three kinds of run. This is what a first QA job looks like on a Monday.
Turning requirements into test scenarios
A requirement describes what should happen; a scenario describes how you will check it. Every requirement needs at least one positive case and one negative one, and a requirement that cannot be tested should be sent back.
The lesson
A requirement is a sentence about what the system should do; a scenario is how you would check it. "Candidates can save a job" becomes: saving works, the saved list shows it, it survives a reload, saving twice does not duplicate, and the count on the profile matches. One sentence, five scenarios.
Read the requirement and write three columns — what must be true, what should happen, what would be wrong — then turn every row into a case. This is where a fresher tester earns their place: a developer reads the code, and you read the requirement and ask about the case nobody has considered.
The mistake is testing only the positive path. "The customer receives the order confirmation" passes; "the customer sees a clear message when the email cannot be sent" is the one that finds the bug. Requirements always leave gaps, and the gaps are your job.
Requirement: "A user may reset a password by email." Scenarios: an email that exists -> email sent an email that does NOT exist -> the SAME generic message (no user leak) a malformed email -> validation error clicking the link after it expires-> a clear "link expired" message submitting twice quickly -> only one email The requirement gives the happy path; you write the rest.Example "The password must be strong" is untestable until somebody writes the rule: at least eight characters, one digit. Now it is a test case, and the person who asked for the rule is the person you ask.
Practice Take one page of a specification from any open-source project and write the positive and negative scenario for each sentence.
Boundary values and equivalence partitioning
Most defects live at the edges: empty, one item, exactly the limit, one past the limit, and the maximum. Testing the middle everywhere else is a waste of a day.
The lesson
Testing every possible input is impossible, so you test the edges of the groups. For a field accepting ages 18 to 60, the interesting values are 17, 18, 19, 59, 60 and 61 — plus a blank — not 25 and 40. This is equivalence partitioning (one value per group) and boundary value analysis (each edge and just past it), and it is the standard answer to "how would you test this field?".
Apply the same thinking to numbers, dates, text lengths, file sizes and quantities. A date range of two days is one group, and end-of-month, leap years and the timezone of the server are the edges. Two minutes of listing groups replaces an hour of random clicking.
The mistake is picking values in the middle of the range because they are comfortable, which is where software almost never breaks. Bugs cluster at zero, one, the maximum, the empty string, and the first and last row of any list.
# Valid range 1..100. # Equivalence: one value from inside is enough - you do not test all 100. # Boundaries: test the EDGES, where bugs actually live. cases = [0, 1, 2, 99, 100, 101] # just outside . edge . just inside def is_valid_age(n): return 1 <= n <= 100 for n in cases: print(n, is_valid_age(n))Example A discount valid for orders of ₹5,000 and above needs four cases around 4,999, 5,000 and 5,001 — not four cases at ₹10,000 with different wording. Same effort, entirely different value.
Practice Find two numeric rules in your own practice sheet and write the boundary set for both, including the off-by-one cases.
Regression, smoke and exploratory testing
Smoke testing answers "is this build worth testing at all"; regression answers "did we break anything that used to work"; exploratory testing is a timed, purposeful hunt for what nobody thought to specify.
The lesson
Smoke testing is the short pass over the critical paths after a build — login, search, apply, pay — to decide whether it is worth testing properly at all. Regression testing is re-running the cases that matter after a change, because the change touched more than its author thought.
Exploratory testing is the opposite of a script: you take a charter — "breach the job-search filters for twenty minutes" — and follow what looks strange, with no expected result written down. It finds the bugs no script does, because it reacts to what the software actually does.
The mistake is re-testing everything after every small change, which is slow and gets skipped under pressure, and then testing nothing. Agree what the change touched, run the smoke set always, and let a risk conversation decide the rest. This is also what automation is for — the repetitive part, not the exploratory part.
Smoke: a quick set after EVERY build - login, main screen, main action. Regression: the full suite that must still pass after any change. Exploratory: unscripted time to find what the written cases did not think of. Rule: automate the smoke and the regression; keep 30-60 min per sprint for exploratory.Example Running the full 400-case regression suite on every build is how teams stall. Tagging thirty cases as the smoke set is how they ship — knowing which thirty is the job.
Practice Pick thirty cases from your sheet to be the smoke set, then run a 30-minute exploratory session on a website and write up what you found.
Module 3Automation with Selenium and a real project3 of 53 lessons
Weeks 5–7 — one language, one framework, one suite. Automation is what separates two testers with the same manual skills.
Your first Selenium script in Python
Selenium drives a real browser. pip install selenium, open a page, find an element, assert its text — four lines, and you have automated a check that used to be manual.
The lesson
Selenium drives a real browser from code: install it, download the driver (or let Selenium Manager do it), and write
driver = webdriver.Chrome(); driver.get(url)— then find an element and act on it:driver.find_element(By.ID, 'email').send_keys('a@b.com'),.click(), and an assertion on what appeared.Structure it as a script that does one journey end to end — open the site, search jobs, open the first result, check the apply button exists — and print a clear pass or fail per step. That single script is a credible automation portfolio item, and it is the same shape as every test suite you will ever write, just bigger.
The mistake is writing a hundred assertions before the first one runs green. Get one line working against a real page, then add the next. Selenium errors say exactly which selector failed and what it waited for; the first one is the only one worth reading.
from selenium import webdriver from selenium.webdriver.common.by import By driver = webdriver.Chrome() try: driver.get("http://localhost:3000/login") driver.find_element(By.ID, "email").send_keys("a@b.com") driver.find_element(By.ID, "password").send_keys("correct-password") driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click() heading = driver.find_element(By.TAG_NAME, "h1").text assert heading == "Dashboard", heading finally: driver.quit()Example driver.get("https://example.com") then find_element(By.TAG_NAME, "h1").text gives you a string you can assert on. Everything else in Selenium is a variation of find, act, assert.
Practice Automate one signup-form check: open the page, type into three fields, submit, and assert the confirmation text.
Waits and selectors that survive
A hard-coded sleep makes a suite slow and still flaky. An explicit wait on a condition — visibility, presence, clickability — makes it fast and stable, and choosing the right condition is the whole craft.
The lesson
Modern pages build themselves with JavaScript, so an element exists a moment after the page loads.
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'apply')))waits for the thing you actually need instead oftime.sleep(5), which is slow when it is fast and fails when it is slow.Choose selectors a developer would keep: an id, a
data-testidattribute, or a stable class — notdiv:nth-child(3) > span, which breaks the next time somebody reorders the layout. Ask for test ids in the code if you can; it is a conversation a junior tester can genuinely start.The mistake is CSS paths copied from the browser inspector, which are tied to today's markup. The second is a fixed sleep everywhere, which makes a suite take twenty minutes and still flake. Waits that express the condition are both faster and more honest.
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # Bad: sleeping, and a selector tied to layout # driver.find_element(By.XPATH, "//div[3]/div[2]/button") # Good: wait for the CONDITION, then act button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "[data-testid=apply]")) ) button.click()Example WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "dashboard"))) waits only as long as it needs to. time.sleep(5) is either too short on a slow build or five wasted seconds on every fast one.
Practice Convert three of your hard sleeps into explicit waits and run the suite twice in a row to prove both passes are genuine.
A portfolio suite with a report
A runnable suite with a readable report is the most convincing artefact a fresher QA can show: it proves you write code and that you think in checks.
The lesson
Take the scripts you have, put them in a folder with a
requirements.txtand a README, and run them withpytest. Pytest finds every file namedtest_*.pyand every function namedtest_*, runs them, and gives you a pass and fail summary you can show in an interview.Organise by journey rather than by page:
test_search.py,test_apply.py,test_login.py. Add a screenshot on failure — pytest hooks make it a dozen lines — so you have evidence of the bug as well as the report. Then put it on GitHub and describe it in three lines on your resume.The mistake is a project that only asserts that pages load. Assertions on behaviour — the count went up, the message appeared, the field is disabled — are what make it look like testing rather than browsing. Five real checks beat fifty page-loads.
# tests/test_jobs.py run: pytest -q --html=report.html import pytest from pages.jobs import JobsPage @pytest.mark.parametrize("keyword,minimum", [("analyst", 1), ("zzz", 0)]) def test_search_results(driver, keyword, minimum): page = JobsPage(driver).open() assert len(page.search(keyword).results()) >= minimum def test_apply_requires_login(driver): page = JobsPage(driver).open().open_first_job() page.apply() assert page.current_path() == "/login"Example Six tests over a public practice site, run with pytest, producing a pass/fail report with screenshots on failure, is plenty — and the screenshot-on-failure detail is what an interviewer remembers.
Practice Build the suite, push it with a README explaining how to run it, and save one real failing run to show what your reports look like.
Module 4Testing behind the screen4 of 53 lessons
APIs, databases and the data you test with, because most bugs never touch the screen.
Testing a REST API with pytest
An API test calls the endpoint and checks the status code and the body, without a browser. It runs in milliseconds, so you can cover far more cases than a UI test ever could.
The lesson
Test the status code AND the shape of the body: a 200 that returns an error message inside the JSON is still a failure to the user.
Test the negative cases hardest - a missing field, an expired token, a duplicate record - because the happy path is the one the developer already clicked through by hand.
import requests BASE = "https://jsonplaceholder.typicode.com" def test_list_posts(): r = requests.get(BASE + "/posts", timeout=10) assert r.status_code == 200 assert isinstance(r.json(), list) def test_missing_post_is_404(): r = requests.get(BASE + "/posts/999999", timeout=10) assert r.status_code == 404Example requests.post(.../login, json=...) then assert r.status_code == 200 and assert "token" in r.json() checks the contract the front end depends on.
Practice Write three API tests against a public test API: one success, one validation failure, one unauthorised.
Database and test-data checks
Some rules cannot be seen from outside: a status that must move through an allowed set, an order total that must equal its lines. Query the database and assert the rule directly.
The lesson
Point automated database tests at a throwaway database seeded by the test itself, never at production - a test that writes to live data is a test that will one day cause the incident.
Test data should be created by the test, not borrowed from whatever was in the table, or the suite passes on Monday and fails on Friday for no visible reason.
-- every application must point at a real job SELECT a.id FROM applications a LEFT JOIN jobs j ON a.job_id = j.id WHERE j.id IS NULL; -- every order total must equal the sum of its lines SELECT o.id, o.total, SUM(l.price) AS line_total FROM orders o JOIN order_lines l ON l.order_id = o.id GROUP BY o.id, o.total HAVING o.total <> SUM(l.price);Example A query counting orders whose total is not the sum of their line items should return zero rows - a one-line test for a rule no UI test can prove.
Practice Write two SQL checks: one for an orphan row, one for a total that does not match its lines.
A performance smoke test
A performance test answers one question: does the page still answer in a reasonable time when several people use it at once. You do not need a load-testing platform to find the obvious slow case.
The lesson
Start with the endpoint that already feels slow to you, at the load you actually expect, and compare against a number you chose in advance - not against a feeling after the fact.
The bug you are hunting is the one that grows with data: a missing index, an N-plus-one query, an unbounded list. A smoke test catches those; a full load test is for later.
import statistics, time, requests def test_search_is_fast_enough(): times = [] for _ in range(50): t0 = time.perf_counter() requests.get("http://localhost:3000/api/jobs?q=analyst", timeout=10) times.append(time.perf_counter() - t0) median = statistics.median(times) assert median < 1.0, f"median {median:.2f}s" print(f"median {median:.3f}s worst {max(times):.3f}s")Example Time fifty requests to the search endpoint and assert the median under one second; that catches the query that was fine with ten rows and dies with ten thousand.
Practice Measure the response time of one endpoint under a repeated call and write down the median and the worst case.
Module 5Working like a QA engineer5 of 53 lessons
A test strategy, bug triage with numbers, and a portfolio a recruiter can open.
Writing a test plan and strategy
A test plan says what you will test, what you will not, and how you will know it is done. It is one page, and it is what turns "I clicked around" into a decision someone can review.
The lesson
The out-of-scope list is the most useful part: it is the sentence that protects you when somebody later asks why the untested corner shipped.
Keep exit criteria numeric - counts of open bugs by severity - so "we are ready" becomes a fact rather than an opinion at the release meeting.
Test plan - Job alerts feature In scope: opt-in, digest job, opt-out, duplicate guard Out of scope: admin editing of templates Environments: Chrome, Firefox, 1 mid-range Android Test types: functional, regression, one API check, smoke Exit criteria: 0 open S1/S2, smoke green on the release build Risks: email delivery is external - test with a sandboxExample In scope: login, search, apply. Out of scope: the admin panel. Exit criteria: no open S1 or S2, and smoke passes on the release build.
Practice Write a one-page test plan for one feature with in-scope, out-of-scope and exit criteria.
Bug triage, metrics and the words that get read
A tester is judged by the bugs that got fixed, not the bugs that got filed. Triage is short, ordered by severity and priority, and every report names the person who needs to act.
The lesson
Track escape rate too: bugs found in production that the tests should have caught. It is the honest measure of whether the suite is doing its job.
Never blame in the report. "The form accepts a bad date" moves a fix; "the developer forgot to validate" moves nothing and costs you the next conversation.
Week 32 - triage note Raised 12 · Fixed 9 · Open 3 (2 x S1, 1 x S3) Oldest open: S1 login cookie, 5 days - blocked on staging config Escape this week: 1 (date validation) - added a regression case Ship recommendation: hold until the two S1s are closed.Example A weekly note: 12 raised, 9 fixed, 2 S1, oldest open 5 days - three numbers that show whether the build is safe to ship.
Practice Write a one-paragraph triage note for a week of testing, with the counts and the oldest open bug.
A portfolio suite a recruiter can open
One repository with a readable test suite, a sample report and a README beats a certificate. It shows the work, and it can be run in front of an interviewer.
The lesson
Structure the repo so a stranger understands it in a minute: tests/ for the cases, pages/ for the helpers, and a README that says what it covers and how to run it.
Mention the one thing it does not cover. Honesty about the gap is what makes the rest of the suite believable in an interview.
# tests/test_jobs.py run: pytest -q --html=report.html import pytest from pages.jobs import JobsPage @pytest.mark.parametrize("keyword,minimum", [("analyst", 1), ("zzz", 0)]) def test_search_results(driver, keyword, minimum): page = JobsPage(driver).open() assert len(page.search(keyword).results()) >= minimum def test_apply_requires_login(driver): page = JobsPage(driver).open().open_first_job() page.apply() assert page.current_path() == "/login"Example pytest --html=report.html produces a shareable report, and the README explains how to run it in two commands.
Practice Put your best ten tests in one repo with a README and a generated HTML report, and push it.
About Software Testing — Manual and Automation
Find, describe and prove defects on purpose — and then automate the checks so the suite runs without you.
Non-IT and IT graduates alike, and anyone who wants into software quality assurance, which is one of the shortest roads from a fresher to a first offer. No programming background is assumed, and none is left behind.
What you will be able to do at the end
- Write a bug report a developer can reproduce without asking a single question
- Turn a requirement into test scenarios, boundary values and negative cases
- Run smoke, regression and exploratory testing on purpose rather than by habit
- Write and run a Selenium script in Python against a real web page
- Handle waits and selectors so a suite passes twice in a row, not once by luck
- Publish a small test suite with a report you can show in an interview
How the course is structured
3 modules and 9 lessons, arranged so each one ends with something you have built. Every lesson carries a worked example and a practice task — the practice is the course, the reading is only the setup. Plan for 7 weeks · about 6 hours a week.
The full syllabus — every lesson, its example and its practice task — is in the Course curriculum below. Nothing is locked and nothing needs an account.
Your weekly routine
- Three sessions a week: one reading, one writing test cases by hand, one writing code.
- Run your automated suite at the end of every week and fix whatever went flaky.
- Keep a one-page log of every defect you find, with its severity and priority.
What you will have built by the end
- A 20-case test sheet for a real signup flow, with boundaries and negative cases
- Six automated Selenium tests with reports and a screenshot on failure
- Five bug reports written in the format a developer can act on without asking
Where this leads for a fresher
- QA engineer / test engineer trainee
- Manual tester at a services company, then automation after six months
- Product-support roles that own reproduction and triage
- Automation tester in product teams running CI
Titles vary between companies; the evidence does not. A deployed project, a set of queries you can explain, or a case study with real testing behind it is what a fresher interview has to work with.
Frequently asked questions
Do I need to be good at coding to be a tester?
Not to start, and yes to grow. Manual testing is the entry point and it is genuinely skill-based. Automation is where the salary and the seniority are, and it is why this course includes a real Selenium project rather than a chapter about it.
Is testing a good career or a dead end?
It is one of the fastest entries into a product team for a fresher, and there are three clear paths out of it: automation and quality engineering, product management, and business analysis. Each of those wants exactly the structured thinking this course trains.
Is this course free?
Yes. Every lesson, example and practice task is free to read, and there is nothing to buy to finish the suite.
Which is asked more in interviews — manual or automation?
Both, and in that order. The manual questions come first because they test whether you think. Two of the Career Portal tools are built for exactly this: the Placement Test Centre for the written round and the free Career Pulse check for where your overall readiness really stands.
Not sure which of these you need first? The free Career Pulse check scores your skills, communication and goal clarity in about three minutes and tells you which gap to close first. Take the free check.