Skip to main content
Published / updated

Testing and Quality Basics

Before you start

You need: acceptance criteria (Article 02) and environments (Article 04).

Time: about 45 minutes, plus the practice.

Learning objective

Design test scenarios that cover more than the happy path, and write a defect report a developer can act on immediately.

Topics

  • Verification and validation
  • Test levels
  • The three error classes
  • Designing scenarios
  • Boundary and negative testing
  • Regression
  • Writing a defect report
  • Severity and priority
  • The pre-handoff checklist

Verification and validation

QuestionAnswered by
VerificationDid we build it right?Tests against the acceptance criteria
ValidationDid we build the right thing?UAT — the users

Software can pass every test and still be wrong. The fee report may calculate exactly what was specified, and the specification may not be what the accounts team needed. Verification cannot catch that; validation can.

Both are needed. QA verifies; the office administrator validates.

Test levels

LevelTestsWho writes itSpeed
UnitOne method in isolationDeveloperMilliseconds
IntegrationSeveral parts together — service and databaseDeveloperSeconds
System / end-to-endThe whole flow through the UIQAMinutes
User acceptanceReal users, real scenariosBusiness usersManual
// Unit — the grading rule alone, no database
[Fact]
public void GetResult_AbsentStudent_ReturnsAbsent()
{
ExamResult result = new ExamResult();
result.IsAbsent = true;
result.MarksObtained = null;

Subject subject = new Subject();
subject.PassingMarks = 35;

string outcome = new GradingService().GetResult(result, subject);

Assert.Equal("Absent", outcome);
}

Most tests should be unit tests. They are fast, they pinpoint the failure, and they run on every build. End-to-end tests are valuable but slow and brittle — a hundred of them takes twenty minutes and one UI change breaks thirty.

A test that needs a database and a browser to check a grading rule is testing the wrong thing at the wrong level.

The three error classes

ClassWhen it appearsFound by
Compiler errorBefore the program runsThe compiler
Runtime errorWhile runningAn exception, a crash
Logical errorNever announces itselfTesting, or a user complaint
int marks = "85"; // compiler — never runs
int marks = int.Parse(rollNumber); // runtime — FormatException
decimal balance = total + paid; // logical — should be minus

The logical error is the dangerous one. It compiles, it runs, it produces a plausible number, and only a person checking the arithmetic notices. A student who has paid ₹12,000 against ₹15,000 shows a balance of ₹27,000 rather than ₹3,000.

Testing exists primarily for the third class. The first two announce themselves.

Designing scenarios

Most defects live outside the happy path. For "search students by roll number":

CategoryScenarioExpected
Happy pathSearch NCA-2024-0012, which existsThat student is returned
EmptySearch with an empty boxValidation message, no request
No matchSearch NCA-9999-9999"No students found"
WhitespaceSearch " NCA-2024-0012 "Same as trimmed — found
CaseSearch ravi and RAVISame results
PartialSearch RavAll active students matching
Special charactersSearch O'BrienFound; no error
Very long input500 charactersHandled, no crash
Inactive studentSearch a transferred studentExcluded unless requested
Other schoolSearch a roll number from School 2Not found
UnauthorisedSearch as a Student role403
Volume800 studentsUnder two seconds

Twelve scenarios; one is the happy path. That ratio is normal.

"Other school" is the scenario nobody writes and everybody needs. Testing that something is not returned is as important as testing that something is — and it is the only way to catch a missing tenant filter, which produces no error.

The apostrophe test is not pedantry. O'Brien breaks string-concatenated SQL, and that is how SQL injection is discovered by accident.

Boundary and negative testing

Defects cluster at boundaries. For a passing mark of 35 out of 100:

InputExpectedWhy test it
34FailJust below
35PassExactly on
36PassJust above
0FailMinimum
100PassMaximum
101RejectedAbove maximum
-1RejectedBelow minimum
nullAbsentThe absent case

>= written as > fails only at exactly 35. A test at 90 and a test at 20 both pass, and a student on exactly the pass mark is failed. This is the single most common off-by-one defect, and one boundary test catches it.

Negative testing is checking that the wrong things are rejected:

  • Letters in a marks field
  • A negative fee payment
  • A payment larger than the balance
  • A duplicate roll number
  • A teacher requesting the salary report
  • A request with an expired token

A system that accepts a payment of −₹5,000 has passed every positive test.

Regression

A regression is something that used to work and no longer does.

Sprint 13: Fee receipt printing works
Sprint 14: Discount handling added
Sprint 14: Fee receipt printing now omits the discount line

The new feature broke the old one. Nobody tested the old one, because it was already working.

This is why automated tests matter. Manually retesting everything each sprint is impossible; a test suite does it in ninety seconds on every commit.

A regression checklist for the School system — the flows that must work in every release:

[ ] Sign in as each role
[ ] Student list loads and is filtered to the signed-in school
[ ] Student search by roll number and by name
[ ] Add a student; duplicate roll number is rejected
[ ] Record a fee payment; the balance updates
[ ] Fee receipt prints with the discount line
[ ] Enter exam marks, including one absent student
[ ] Result card shows Absent, not Fail, for that student
[ ] Class report average excludes absentees
[ ] Teacher role cannot open the salary report

Write this list once and run it every release. Automate what you can; the rest takes twenty minutes and prevents the phone call.

Writing a defect report

Title Class average includes absent students as zero

Environment QA, build 2.4.0-rc3
Role Signed in as principal@nca.test (School 1)

Steps 1. Open Exams → Sprint Assessment → 10th-A
2. Mark 5 of the 40 students absent
3. Enter marks for the remaining 35
4. Open the class result report

Expected Average calculated over the 35 students who appeared
Actual Average calculated over all 40, counting absentees as 0
Report shows 54.2; correct value is 61.9

Evidence Screenshot attached
SQL: SELECT AVG(MarksObtained) FROM ExamResult
WHERE ExamId = 5 AND IsAbsent = 0 → 61.9

Frequency Every time; reproduced 3 times
Scope Every class with at least one absent student
Since Build 2.4.0-rc1; worked in 2.3.2
Severity High — printed report cards will show wrong averages
FieldWhy it matters
StepsWithout them the developer cannot reproduce it
ExpectedWithout it they must guess what correct means
ActualWith the real numbers, not "it's wrong"
EvidenceA screenshot or a query turns opinion into fact
FrequencyEvery time, or one in ten — different causes
ScopeOne class or all of them
SinceWhich build — makes it findable with git bisect

"Since 2.4.0-rc1; worked in 2.3.2" is the most valuable line in the report. It converts an open investigation into a search through a known set of commits.

A defect report without "expected" is not a report; it is a complaint.

Severity and priority

MeansDecided by
SeverityHow bad the impact isQA
PriorityHow soon it must be fixedProduct Owner

They are independent.

ExampleSeverityPriority
Cross-school data exposureCriticalImmediate
Class average wrongHighHigh — report cards are due
Logo misaligned on the receiptLowLow
Typo on the login pageLowHigh — every user sees it
Crash in a feature nobody uses yetCriticalLow

A high-severity, low-priority defect is a normal outcome, and so is the reverse. Arguing that severity should drive the order is arguing with the person whose job it is to decide.

The pre-handoff checklist

Before you hand anything to QA:

  • Every acceptance criterion works — check them one by one
  • The empty case, the no-match case and the error case
  • At least one boundary value
  • One negative case — invalid input is rejected
  • Signed in as a role that should not have access
  • Data from another school is not returned
  • The absent case, wherever marks are involved
  • No Console.WriteLine or debug code left in
  • Tests written and passing
  • Run once from a clean state, as a new user would

Ten minutes here saves a round trip that takes two days. A defect found by QA goes into the tracker, gets triaged, comes back, is fixed, is redeployed and is retested. The same defect found by you is a one-line change before you open the pull request.

"It works" usually means "the happy path works". Check the other eleven scenarios.

Where this goes wrong

The mistakeWhat it misses
Testing only the happy pathEleven of twelve realistic scenarios
No boundary test at the pass mark> written for >= fails only at exactly 35
Never testing that something is not returnedA missing tenant filter, which produces no error
Not testing an unauthorised roleA teacher reaching the salary report
A defect report with no "expected"The developer has to guess what correct means
"It's wrong" instead of the numbersNobody can reproduce it
Not recording which build broke itAn open-ended search instead of ten commits
Assuming old features still workRegressions ship every sprint

Testing that something is absent is as important as testing it is present, and it is the check almost nobody writes.

Common mistakes

  • Testing only the happy path
  • No boundary test at the exact pass mark
  • Never testing that something is not returned
  • Not testing with another school's data
  • Not testing an unauthorised role
  • Defect reports with no expected result or no steps
  • "It's wrong" instead of the actual numbers
  • Not recording which build introduced the defect
  • Confusing severity with priority
  • Assuming existing features still work
  • Handing over without running the acceptance criteria yourself

Practice

The course exercises are classify compiler, runtime and logical errors and read a simple API error response.

  1. Write one example each of a compiler, runtime and logical error in the School system.
  2. Explain why only the third needs testing to find.
  3. Design twelve test scenarios for "record a fee payment", covering happy path, empty, boundary, negative, unauthorised and cross-school.
  4. For a passing mark of 35, list every boundary value you would test and say what > instead of >= would break.
  5. Write five negative test cases for the fee payment form.
  6. Take a feature you have built and write its regression checklist.
  7. Write a defect report for the class-average bug, with every field filled in.
  8. Rewrite a report you would normally send — "the report is wrong" — into a usable one.
  9. Assign severity and priority to five defects, including one where they diverge sharply.
  10. Explain why a typo on the login page can be low severity and high priority.
  11. Run the pre-handoff checklist against your last piece of work and note what it catches.
  12. Find an API that returns a 400 and read its response body. Say what the caller did wrong.
  13. Test a form as an unauthorised role, using the browser's Network tab to call the endpoint directly.

Exercise 3 is the one to repeat for every feature you build. Exercise 11 is the habit that makes QA like you.

You can now

  • Design scenarios covering boundaries, negatives and permissions
  • Test that the wrong data is not returned
  • Write a defect report with steps, expected, actual and scope
  • Tell severity from priority
  • Build a regression checklist for a feature
  • Check your own work before handing it to QA

Review questions

  1. What does validation catch that verification cannot?
  2. Why does the boundary at exactly the passing mark deserve its own test?
  3. Why is testing that something is not returned as important as testing that it is?
  4. How can a defect be high severity and low priority at the same time?

Next: Release and support