Software Testing Concepts β In-Depth (NPTEL notes, expanded)¶
Companion to 04_Manual_Testing_SDLC_STLC.md. This file takes the concepts you noted from the NPTEL Software Testing course (Prof. Rajib Mall, IIT Kharagpur) and expands each with a definition β plain explanation β where/why it's used β example. It also adds closely-related concepts that usually get asked alongside them (marked β Added).
Reading tip: white-box coverage (Part 3) is the meatiest and most-tested part β go slow there. Every concept ends with the interview angle.
Contents 1. QA/Testing basics (fault model, coverage, test case vs data, negative tests, test plan, strategy, unit testing) 2. Black-box testing (EP, BVA, robustness, special-value, decision table, combinatorial/pairwise, t-way) 3. White-box testing (statement/branch/condition/MC-DC/path coverage, cyclomatic complexity, data-flow, mutation) 4. Integration testing (top-down, bottom-up, sandwich) 5. System testing (alpha/beta/acceptance, performance family) 6. Test reporting & maintenance (summary report, regression, error seeding, automation choices) 7. Object-Oriented (OO) testing (unit, encapsulation/inheritance/polymorphism, state-based)
PART 1: QA / TESTING BASICS¶
1.1 Fault model¶
Definition. A fault model is a list of the kinds of faults (bugs) you expect in a program, used to guide where and how to test. (Reminder from file 04: a fault/defect is the flaw in the code; a failure is what the user sees when it runs.)
Why/where used. You can't test everything (exhaustive testing is impossible), so you target the faults most likely to occur. Each test-design technique is built around a fault model β e.g., boundary value analysis assumes "faults hide at the edges of ranges."
Example. For an age field (1β120), the fault model says: off-by-one errors at boundaries, wrong handling of negatives/zero, non-numeric input. Your tests then target exactly those.
Interview angle: "Every test technique is really a bet on a fault model β BVA bets on boundary faults, decision tables bet on combination faults. I pick techniques by which faults are most likely and most costly."
1.2 Coverage-based testing¶
Definition. Coverage-based testing measures how much of something your tests exercise β lines of code, branches, requirements β and uses that as a stopping/adequacy criterion ("we've tested enough when X% is covered").
Why/where used. It answers "when do we stop testing?" objectively, and reveals untested parts. Used heavily in white-box testing and safety-critical software (where high coverage is mandated).
Example. "Our exit criterion is 100% statement coverage + 90% branch coverage." If a branch is never taken by any test, coverage tooling flags it β that's untested logic.
β Coverage-based vs fault-based testing (they're the two big philosophies): - Coverage-based β measure structural coverage (statements, branches, paths). "Did we execute everything?" - Fault-based β measure ability to detect faults (e.g., mutation testing, error seeding). "Would our tests actually catch a bug?" - Key point: 100% coverage does not mean bug-free β you can execute a line without asserting its output is correct. Fault-based testing checks the tests' detecting power, not just execution.
1.3 How to identify the coverage of a test suite¶
Definition/how. You run the tests under a coverage tool (JaCoCo/Cobertura for Java, coverage.py for Python, Istanbul for JS). The tool instruments the code, records which statements/branches ran, and reports a percentage + a highlighted "not covered" list.
Why/where used. To find gaps ("this else-branch is never tested") and to enforce a coverage gate in CI.
Example. coverage run -m pytest && coverage report β "Module discount.py: 82% β lines 45β48 (the bulk-order branch) never executed." β add a test for a bulk order.
1.4 Test case vs test data (common confusion)¶
- Test case = the full instruction to test one thing: ID, precondition, steps, input data, and expected result.
- Test data = just the input values fed into a test case (and any DB setup it needs).
Example. Test case = "TC_LOGIN_02: enter email + password, click Login, expect Dashboard." Test data = email=user@x.com, password=Test@123. One test case can run with many sets of test data (that's data-driven testing).
Remember: the test case is the recipe; the test data is the ingredients.
1.5 Negative test cases¶
Definition. A negative test case checks that the system handles invalid/unexpected input gracefully β rejects it with the right error, doesn't crash, doesn't corrupt data. (Positive tests check the happy path; negative tests check the "unhappy" paths.)
Why/where used. Real users and attackers send bad input. Most serious bugs live in error handling, not the happy path.
Example. Login field: positive = valid credentials β dashboard. Negative = empty password β "Password required"; SQL-injection string β safely rejected; 10,000-character email β 400, no crash.
1.6 Why design test cases? (vs ad-hoc testing)¶
Definition/why. Designing test cases up front β each targeting a specific fault type β gives you coverage, repeatability, and traceability. Random/ad-hoc testing misses systematic gaps and can't be re-run reliably.
Explanation. Each test-design technique targets different faults: EP/BVA β input-domain faults; decision tables β logic-combination faults; state-based β sequence faults. Designing deliberately means you cover all the fault categories, not just the ones you happened to think of.
Example. Instead of "poke the form randomly," you design: 1 valid + 2 invalid partitions (EP), boundary values (BVA), and a decision table for the discount rules β now you can prove what you covered.
1.7 Test Plan (what it documents)¶
Definition. A Test Plan is the document created before testing starts that defines the what, how, who, and when of testing.
It documents: - Features to be tested and features NOT to be tested (scope + explicit out-of-scope). - Test strategy / approach (which types of testing, how much effort each). - Entry criteria (when testing can start) and suspension / resumption criteria (when to pause, e.g., "if smoke test fails, stop") and exit / stopping criteria (when testing is done). - Test effort (estimation), test schedule, resources, environment, risks, deliverables.
Why/where used. It's the contract for the testing activity β aligns the team, sets expectations, and defines "done."
Interview angle β suspension vs exit criteria: suspension = temporarily stop (blocker found, environment down); exit/stopping = testing is complete (all planned tests run, no open critical bugs, coverage met).
1.8 Test strategy (part of the plan)¶
Definition. The test strategy decides which types of testing to use and how much effort to spend on each.
Three broad strategies: - Black-box (functional) β test against requirements, no code knowledge (Part 2). - White-box (structural) β test against code structure (Part 3). - Usage / usage-based testing β test the way real users actually use it (weight tests by real usage frequency β a.k.a. operational profile testing; heavily used for reliability estimation).
Example. "70% effort on black-box functional, 20% white-box on the payment engine (high risk), 10% usage-based on the top-5 user journeys."
1.9 Unit testing¶
Definition. Unit testing = testing an individual method, module, class, or component in isolation β the smallest testable piece β usually by developers.
Why/where used. Bugs caught here are cheapest to fix; unit tests run fast and pin down exactly where a fault is. Isolation is achieved with stubs/mocks for the unit's dependencies.
Example. Test calculateTax(income) alone with a table of inputs β expected taxes, mocking the DB it would normally read rates from.
PART 2: BLACK-BOX TESTING (functional / structural-free)¶
Definition. Black-box testing designs tests from the requirements/specification only β you treat the program as a box you can't see inside. Also called functional testing.
What's hard about black-box testing. The input space is usually enormous (or infinite), and you have no code to guide you β so the challenge is choosing a small set of inputs that still finds most faults. That's exactly what the techniques below do: shrink a huge input space to a smart few.
2.1 Equivalence Class Partitioning (ECP)¶
Definition. Split the input domain into groups (classes) that should behave the same, then test one representative per class β testing more from the same class adds little.
The "1 valid + 2 invalid" rule. For a valid range, you typically get one valid class and two invalid classes (below-range and above-range).
Why/where used. Massively reduces test count while keeping coverage of behaviors.
Example. Field accepts 1β100:
- Invalid-low: 0 (or β5)
- Valid: 50
- Invalid-high: 101
β 3 tests instead of testing every number.
2.2 Boundary Value Analysis (BVA)¶
Definition. Test the edges of each equivalence class, because faults cluster at boundaries (developers mix up < vs <=).
Why/where used. Boundaries are the single most fault-prone spot. Always pair BVA with ECP (ECP picks the classes, BVA picks the edge values).
Example. Range 1β100:
- 2-value BVA: 1, 100 (the boundaries)
- 3-value BVA: 0,1,2 and 99,100,101 (edge Β± just inside/outside)
2.3 Robustness testing (extension of BVA)¶
Definition. BVA plus values just outside the valid range β min β 1 (less than min) and max + 1 (greater than max) β to check the system handles out-of-range input gracefully.
Why/where used. It's BVA focused on the error-handling side β the negative-testing version of boundaries.
Example. Range 1β100 β robustness tests add 0 (minβ1) and 101 (max+1) and check for a clean rejection, not a crash. Values tested: 0, 1, 2, β¦, 99, 100, 101.
2.4 Special-value testing¶
Definition. Test with known "tricky" values the tester's experience flags as fault-prone β independent of the formal partitions.
Why/where used. Some values break code regardless of range logic β a form of error guessing.
Example. 0, empty string, negative numbers, very large numbers, null, whitespace-only, leap-year date Feb 29, 2^31β1 (int overflow), Unicode/emoji, currency 0.00.
2.5 Decision Table testing (DTT)¶
Definition. For features driven by combinations of conditions (business rules), build a table of every condition-combination and its expected action β then make one test per rule/column.
Why/where used. Guarantees you don't miss a combination in complex logic (eligibility, pricing, permissions).
Example. Discount rule β conditions: Member? (Y/N), Order β₯ βΉ5000? (Y/N):
| Rule | Member | Order β₯ 5000 | Discount |
|---|---|---|---|
| 1 | Y | Y | 20% |
| 2 | Y | N | 10% |
| 3 | N | Y | 5% |
| 4 | N | N | 0% |
β 4 test cases, one per rule. (2 conditions β 2Β² = 4 combinations.)
2.6 Combinatorial testing & the explosion problem¶
Definition. Combinatorial testing deals with features that have many parameters, each with several values β where testing all combinations explodes (10 parameters Γ 3 values = 3ΒΉβ° β 59,000 combos).
The insight that saves you: most bugs are triggered by the interaction of only a few parameters, not all of them at once.
Pairwise (2-way) testing¶
Definition. Generate the smallest set of tests such that every pair of parameter-values appears together at least once.
Why/where used. Empirically, a large share of interaction faults involve just 2 parameters β so pairwise catches most of them with a tiny fraction of the full combinations.
Example. 3 parameters, each with 3 values (Browser: Chrome/Firefox/Safari; OS: Win/Mac/Linux; Plan: Free/Pro/Team) = 3Γ3Γ3 = 27 full combinations β pairwise = 9 tests cover every browser-OS, browser-plan, and OS-plan pair. Tools: PICT (Microsoft), ACTS (NIST), or allpairspy.
Interaction testing & t-way / t-way interaction fault¶
Definition. Interaction testing generalizes pairwise: t-way testing covers every combination of any t parameters. A t-way interaction fault is a bug that only appears when t specific parameters take specific values together.
Why/where used. NIST studies found most faults are 1-to-6-way, with the majority β€ 2β3 way β so 2-way (pairwise) or 3-way testing catches the vast majority at a fraction of the cost.
Example. A bug that only fires when Safari + Linux + Pro plan all hold is a 3-way interaction fault β pairwise might miss it, so you'd use 3-way testing for high-risk areas.
2.7 How many test cases β min, max, and coverage? (your question)¶
- Maximum = all combinations / all input values (usually infeasible β the explosion).
- Minimum meaningful = driven by the technique's coverage goal:
- ECP β 1 per class.
- BVA β ~2β3 per boundary.
- Decision table β 1 per rule (2βΏ rules for n conditions).
- Pairwise β enough to cover all pairs (a tool computes it).
- Ensuring coverage = pick the technique whose fault model matches the feature, then measure (requirements coverage via an RTM, code coverage via a tool). "Enough" = the exit criterion in the test plan is met.
Interview angle: "There's no single number β the minimum is set by the coverage criterion I choose (e.g., all decision-table rules), and I confirm it with an RTM for requirement coverage and a coverage tool for code coverage."
PART 3: WHITE-BOX TESTING (structural)¶
Definition. White-box (structural / glass-box) testing designs tests from the internal code structure β you can see the code and aim to exercise its statements, branches, and paths. Measured with coverage.
The coverage hierarchy (weakest β strongest) β β Added, very commonly asked:
Statement < Branch (Decision) < Condition/Decision < MC/DC < Multiple-Condition
Each stronger level subsumes the weaker (100% of a stronger level guarantees 100% of the weaker). Stronger = more tests = more cost.
- Two caveats (so you're precise): (1) Condition coverage alone does NOT subsume branch/decision coverage β you can make each atomic condition go T and F without the whole decision going both ways; that's why "Condition/Decision (C/DC)" combines both. (2) Path coverage is not a clean top of this same chain β it subsumes statement and branch, but because it treats a compound condition as one decision it does not necessarily subsume MC/DC or multiple-condition coverage. Think of path coverage as a separate, very strong criterion, not "the strongest of this list."
We'll use this tiny Euclid's GCD program as the running example:
int gcd(int a, int b) { // 1
while (b != 0) { // 2 (decision)
int t = b; // 3
b = a % b; // 4
a = t; // 5
}
return a; // 6
}
3.1 Statement coverage¶
Definition. % of executable statements run at least once by the test suite. Goal: execute every line.
Why/where used. The most basic coverage; a weak but necessary floor. A line never executed is a line never tested.
Example. gcd(12, 8) enters the loop β executes lines 1β6 β 100% statement coverage with one test. But gcd(5, 0) alone skips the loop body (lines 3β5) β those statements uncovered. Weakness: executing a line β checking its result is correct.
3.2 Branch / Decision coverage¶
Definition. % of decision outcomes taken β every decision (if/while) must go both true and false at least once.
Why/where used. Stronger than statement coverage; catches "the else-branch was never tested" bugs. Common industry target.
Example. For GCD, the while (b != 0) must be true (loop runs) and false (loop exits). gcd(12, 8) does both (runs several times, then exits) β 100% branch coverage. Note: 100% statement coverage does not guarantee branch coverage β an if with no else can be fully statement-covered while its false branch is never taken.
3.3 Atomic conditions & Condition coverage¶
Definition. An atomic condition is a single boolean sub-expression with no &&/|| inside it. In if (A && B), the atomic conditions are A and B.
- Condition coverage = each atomic condition evaluates both true and false at least once.
Why/where used. Decision coverage can miss faults inside compound conditions; condition coverage exercises each sub-condition.
Example. if (age > 18 && hasLicense) β atomic conditions age > 18 and hasLicense. Condition coverage requires each to be true and false across your tests.
3.4 Multiple-Condition Coverage (MCC) β advantages & disadvantages¶
Definition. Test every possible combination of the atomic conditions' true/false values in a decision.
Advantage. The most thorough condition-level coverage β catches every logic combination. Disadvantage. Exponential β n atomic conditions β 2βΏ combinations. For a decision with 6 conditions that's 64 tests; impractical for complex predicates.
Example. if (A && B) β 4 combos (TT, TF, FT, FF). if (A && B && C && D) β 16. This blow-up is why MC/DC exists.
3.5 MC/DC (Modified Condition/Decision Coverage)¶
Definition. MC/DC requires that each atomic condition independently affects the decision's outcome β i.e., for each condition you show one pair of tests where flipping only that condition flips the result (holding the others fixed).
Why/where used. The practical middle ground between decision coverage (too weak) and multiple-condition coverage (too expensive). Mandated for safety-critical software β e.g., DO-178C Level A (avionics). Needs only about N+1 tests for N conditions (vs 2βΏ for MCC).
Example β decision = A && B (conditions A, B):
- Show A matters: hold B=T, vary A β (A=T, B=T)=T and (A=F, B=T)=F. Flipping A flipped the result β
- Show B matters: hold A=T, vary B β (A=T, B=T)=T and (A=T, B=F)=F. Flipping B flipped the result β
- Test set = { TT, FT, TF } β 3 tests (= N+1, with N=2). Compare: MCC would need 4.
| Test | A | B | A&&B | Demonstrates |
|---|---|---|---|---|
| 1 | T | T | T | baseline |
| 2 | F | T | F | A independently affects outcome (vs test 1) |
| 3 | T | F | F | B independently affects outcome (vs test 1) |
3.6 Path coverage & path-coverage-based testing¶
Definition. Path coverage = execute every possible end-to-end path through the program. Basis path testing = execute all linearly independent paths at least once (a practical subset β you don't need all paths, just an independent-spanning set).
Why/where used. The strongest structural criterion. Full path coverage is usually infeasible (loops β infinite/huge paths), so basis path testing (guided by cyclomatic complexity, below) is the realistic version.
Example. if (A) {β¦} if (B) {β¦} has up to 4 paths (A-then-B combinations). With loops, paths explode β so you test the independent paths, not every path.
3.7 Cyclomatic Complexity (McCabe)¶
Definition. A metric of the number of linearly independent paths through a program's control-flow graph (CFG). Three equivalent formulas: - V(G) = E β N + 2P (E = edges, N = nodes, P = connected components/exits) - V(G) = (number of predicate/decision nodes) + 1 - V(G) = (number of enclosed regions in the CFG) + 1
Why/where used. (1) It's the number of basis-path test cases needed for branch coverage β a lower bound on tests. (2) It measures code complexity (higher = harder to test/maintain; >10 is a common "refactor" threshold).
Example. For if (A && B) X; else Y; treated with one compound decision: 1 predicate node β V(G) = 1 + 1 = 2 β 2 basis paths (the then-path and the else-path) β at least 2 tests for branch coverage.
Interview angle: "Cyclomatic complexity tells me the minimum number of tests for branch/basis-path coverage and flags over-complex methods that need refactoring."
3.8 Data-flow testing¶
Definition. Test based on the life of variables β the paths from where a variable is defined (assigned) to where it is used. Key idea: a def-use (DU) pair = a definition of a variable and a subsequent use of it.
Why/where used. Catches faults that control-flow coverage misses β e.g., a variable used before it's set, or a definition that's never used (dead assignment), or the wrong value flowing to a use.
Coverage criteria: all-defs (each definition reaches some use), all-uses (each def-use pair exercised), all-DU-paths (every path between them).
Example. x = getPrice(); β¦ total = x * qty; β the DU pair is (define x, use x in total). A data-flow test ensures the path from that definition to that use is exercised, catching a bug where x gets overwritten in between.
3.9 Mutation testing (fault-based)¶
Definition. Deliberately inject small artificial faults into the code (each faulty copy = a mutant), then run your existing tests. If a test fails on the mutant, the mutant is "killed" (good β your tests detect that fault). Mutation score = mutants killed Γ· total mutants.
Why/where used. It measures the fault-detecting power of your test suite β not just whether lines ran (coverage) but whether your assertions would actually catch a bug. The gold standard for evaluating test quality.
Advantages. Directly answers "would my tests catch a real bug?"; exposes weak/missing assertions that still hit 100% coverage. Disadvantages/challenges. Computationally expensive (many mutants Γ full test run); the equivalent mutant problem β some mutants are functionally identical to the original, so no test can kill them (they must be identified and excluded manually).
Example. Original if (a > b). Mutants: a >= b, a < b, a == b. A good test with input a = b distinguishes > from >= β kills that mutant. If no test can tell them apart, your suite has a gap. Tools: PIT (Java), mutmut/cosmic-ray (Python).
Coverage vs mutation (say this): "Coverage tells me what code ran; mutation tells me whether my tests would catch a bug in that code. You can have 100% coverage and a 0% mutation score if you never assert anything."
PART 4: INTEGRATION TESTING¶
Definition. Integration testing checks that separately-developed modules work correctly together β the target is interface faults (wrong data passed, mismatched assumptions, bad call order), not logic inside a module (that's unit testing).
Why/where used. Modules can each pass unit tests but still fail when combined (mismatched formats, null handling, timing). Integration testing is where those show up.
4.1 Top-down integration¶
Definition. Integrate and test from the top module downward, replacing not-yet-built lower modules with stubs (fake "called" modules that return canned values). - Pros: high-level design/flow validated early; a working skeleton exists sooner. - Cons: you must write many stubs; low-level modules tested late.
4.2 Bottom-up integration¶
Definition. Integrate from the lowest modules upward, using drivers (fake "calling" modules that invoke the unit). - Pros: low-level utilities tested thoroughly and early; fewer stubs. - Cons: high-level flow validated late; needs drivers.
Stub vs driver: a stub replaces a module that is called (used in top-down); a driver replaces a module that calls (used in bottom-up).
4.3 Sandwich (hybrid) integration¶
Definition. Combine top-down and bottom-up at the same time, meeting in a middle target layer β the top half integrates downward while the bottom half integrates upward.
Why/where used. Gets the best of both (early high-level and low-level testing), at the cost of more stubs and drivers. Good for large layered systems.
β Big-bang integration (for contrast): integrate everything at once then test β fast to set up but faults are very hard to localize; generally discouraged.
PART 5: SYSTEM TESTING¶
Definition. System testing tests the complete, integrated application end-to-end against the requirements β the whole product, as a user would experience it.
5.1 Acceptance-flavored levels¶
- Alpha testing β done in-house (by the org's own testers/staff), simulating real use, before release.
- Beta testing β done by real end users in the real environment, before general release; feedback drives final fixes.
- Acceptance testing (UAT) β the customer/business verifies it meets their needs and formally accepts it.
Example. A banking app: Alpha = QA + internal staff dogfood it; Beta = a pilot group of real customers; Acceptance = the bank's business team signs off before go-live.
5.2 Two broad kinds of system testing¶
- Functionality testing β does it do what the spec says (features)?
- Performance testing β how well does it perform (the "non-functional" qualities below)?
5.3 The performance / non-functional test family (define each)¶
| Test | What it checks |
|---|---|
| Load | Behavior under expected heavy usage (e.g., 1,000 concurrent users). |
| Stress | Behavior beyond limits β push until it breaks, see how it fails and recovers. |
| Volume | Handling large amounts of data (huge DB, big files, long lists). |
| Configuration | Works across supported hardware/software configs (CPU, memory, settings). |
| Compatibility | Works across browsers, OSs, devices, versions. |
| Security | Resists attacks β auth, injection, data leaks (OWASP). |
| Recovery | Recovers correctly after a crash/failure (power loss, network drop). |
| Maintenance | Can be updated/patched/backed-up as designed. |
| Documentation | The docs/help/manuals are accurate and complete. |
| Usability | Is it easy and intuitive to use? |
| Environmental | Handles its physical/operating environment (temperature, network conditions) β relevant for embedded/IoT. |
Load vs Stress (classic): Load = expected peak; Stress = past the breaking point.
PART 6: TEST REPORTING & MAINTENANCE¶
6.1 Test Summary Report (what it specifies)¶
Definition. The document produced at the end of testing that summarizes results for stakeholders. It specifies: - What was tested (scope) and the environment/versions. - Test execution results β number planned / executed / passed / failed / blocked / skipped. - Defects β found, fixed, still open (by severity/priority). - Coverage β requirement and/or code coverage achieved. - Metrics β defect density, pass %, etc. - Exit-criteria assessment and a go/no-go recommendation + sign-off.
Why/where used. It's the evidence base for the release decision.
6.2 Regression testing¶
Definition. Re-running existing tests after any change (bug fix, new feature, refactor) to confirm the change didn't break something that used to work.
Why it's needed. Fixes and new code frequently break unrelated features (the "I fixed A and broke B" problem). Regression testing is your safety net; it's the #1 thing automation targets.
"How many errors are still remaining?" You never know the exact number, but you estimate remaining defects using techniques like error seeding (below), defect density trends, and reliability growth models. Regression also asks: are old, fixed bugs staying fixed?
6.3 Error seeding (and: is it still used in 2026?) β your question¶
Definition. Deliberately inject a known number of artificial errors ("seeds") into the program, then test. From how many seeded errors your testing finds, you estimate how many real errors remain.
The formula (Mills' technique). Seed S errors. Testing finds s of the seeded ones and r real ones. Assuming your tests find both kinds at the same rate:
estimated total real errors β r Γ S / s
(If you found few seeded errors, you've probably found few real ones β many remain.)
Example. Seed S = 20. Testing finds s = 16 seeds and r = 40 real bugs β estimated real total β 40 Γ 20/16 = 50 β about 10 real bugs still remain.
Is it required for latest products in 2026? Rarely used directly in modern industry. It's mostly a teaching/academic technique now. In practice it's been superseded by mutation testing (a more rigorous, automated form of the same "inject faults to measure test quality" idea) plus code coverage, static analysis, and code review. Know the concept for interviews/theory; don't claim it's part of a modern CI pipeline β say "mutation testing is the modern successor."
6.4 Which test cases to automate vs skip β your question¶
Usually automate: - Regression tests (run constantly β biggest ROI). - Smoke/sanity tests (fast build checks). - Stable, repetitive flows and data-driven tests (same steps, many inputs). - Deterministic tests with clear pass/fail. - API tests (fast, stable) and cross-browser/cross-config matrices.
Usually skip / keep manual: - Exploratory and usability testing (need human judgment). - One-off / rarely-run tests (automation cost > benefit). - Tests on unstable, rapidly-changing UI (high maintenance churn). - Tests needing complex human verification (visual aesthetics, CAPTCHA). - Tests run only once before deprecation.
Rule of thumb: automate the high-frequency, high-stability, high-value tests; keep human effort for judgment-heavy, changing, or one-off work.
6.5 Partitioning an existing test suite / mix of test types β your question¶
Definition. For maintenance, you partition a large suite so you don't re-run everything every time: - By change impact β a regression subset covering only the modules affected by a change (test selection). - By type/level β smoke vs full regression vs nightly. - By priority/risk β critical-path tests first.
Typical mix (the test pyramid guideline): ~70% unit, ~20% integration/API, ~10% UI/E2E. Unit tests are cheap/fast/stable β most; UI tests are slow/flaky β fewest. (Exact % varies by project, but "more low-level than high-level" is the principle.)
PART 7: OBJECT-ORIENTED (OO) TESTING¶
OO features (encapsulation, inheritance, polymorphism, dynamic binding) create new fault types that traditional testing misses. This is a favorite advanced-interview area.
7.1 What is a suitable unit for testing an OO program?¶
Definition/answer. The class (not the individual method) is the natural unit of OO testing.
Why. Methods aren't independent β they interact through the object's shared state (fields). A method's behavior depends on which methods ran before it (order matters). So testing a method in isolation misses state-interaction faults; you test the class as a whole, exercising method sequences.
Example. A Stack class: testing pop() alone is meaningless β its result depends on prior push() calls. You test sequences (push, push, pop, pop) against the object's state.
7.2 Challenges in OO testing (overview)¶
- Encapsulation hides internal state β hard to observe whether a method left the object in the right state.
- Inheritance β inherited methods may behave differently in a subclass's context (must decide what to re-test).
- Polymorphism / dynamic binding β the actual method called is decided at runtime, so a single call site can invoke many implementations β each must be tested.
- State-dependent behavior β same method, different result depending on object state β need state-based testing.
7.3 Encapsulation β what to test, challenge, solution¶
- Challenge: private fields/methods are hidden, so you can't easily see if an operation set the internal state correctly (the observability problem).
- What to test: that operations leave the object in valid states; invariants hold.
- Solutions: test via the public interface and observe through public getters; use state-based testing; add test hooks / reflection / "friend" test access sparingly; check class invariants after each operation.
7.4 Inheritance β and "should inherited methods be retested?" β your question¶
- Challenge: a method inherited from a parent runs in the subclass's context β with new/overridden fields and methods around it β so it can behave differently even though its code didn't change. (This causes the "yo-yo problem": control bounces up and down the inheritance hierarchy, making behavior hard to follow.)
- Answer β yes, inherited methods often must be re-tested, in the context of the subclass, when:
- the subclass overrides methods the inherited method calls (dynamic binding changes behavior), or
- the subclass adds/changes state the inherited method depends on.
- If the inherited method is truly independent of anything the subclass changes, re-testing can be reduced β but the safe default is to re-test inherited methods whose behavior could be affected.
Example. Shape.area() calls this.width(). Subclass Square overrides width(). The inherited area() now behaves differently in Square β must be re-tested there.
7.5 Polymorphism & Dynamic binding β what to test¶
Definition. Dynamic binding = the specific method implementation invoked by a call like shape.area() is chosen at runtime based on the object's actual type. Polymorphism = one interface, many implementations.
- Challenge: one call site can bind to many implementations; you can't tell from the code alone which runs. Each possible binding is a distinct path to test.
- What to test: every possible binding at each polymorphic call site (test with each concrete subtype), and that all subtypes honor the expected contract (Liskov substitution β a subtype must be usable wherever its parent is).
Example. render(Shape s) where s can be Circle, Square, Triangle β test render with each concrete type, since each binds to a different area()/draw().
7.6 Abstraction β what to test¶
- Challenge: abstract classes/interfaces have no behavior of their own; behavior lives in concrete implementations.
- What to test: each concrete implementation against the abstraction's contract (same test suite applied to every implementer ensures they all comply).
7.7 State-based testing¶
Definition. Model the object as a state machine (states + valid transitions) and test that events cause the correct transitions β including that invalid transitions are rejected.
Why/where used. OO objects are state-dependent, so many faults are sequence faults ("this only breaks if you call B before A"). State-based testing systematically covers transitions.
Example. An Order object: states New β Paid β Shipped β Delivered. Tests: valid transitions work; invalid ones are blocked (can't go New β Shipped without Paid); can't pay() an already-Delivered order. (This is the same State Transition technique from file 04, applied to an object's lifecycle.)
7.8 OO test process / strategy (putting it together)¶
A common OO test order: 1. Method testing β individual methods (basic correctness). 2. Class/intra-class testing β method sequences against object state (state-based). 3. Integration β interactions between collaborating classes, including polymorphic bindings. 4. System testing β end-to-end.
Focus extra effort where OO faults concentrate: overridden methods, polymorphic call sites, and state-dependent sequences.
PART 8: Q&A / QUIZ DRILL (cover the answer, say it out loud)¶
The NPTEL coverage-based quiz (answer all three):
1. What do you mean by coverage-based testing? β A white-box approach where you measure how much of the code structure your tests exercise (statements, branches, conditions, paths) and use a target % as the "when to stop" criterion.
2. What are the different types of coverage-based testing? β Statement, Branch/Decision, Condition, Condition/Decision, MC/DC, Multiple-Condition, and Path coverage β plus Data-flow coverage (def-use). (Weakβstrong roughly in that order; path is a separate strong one.)
3. How is a specific coverage-based testing carried out? β Instrument the code with a coverage tool, run the test suite, read the % + the highlighted "not covered" lines/branches, then add tests for the gaps until the target is met. Example: statement coverage of gcd() needs one test that enters the loop (gcd(12,8)).
Basics - Q: Coverage-based vs fault-based testing? β Coverage = "did we execute it?"; fault-based (mutation/error-seeding) = "would our tests catch a bug?" 100% coverage β bug-free. - Q: Test case vs test data? β Case = the recipe (steps + expected result); data = the input ingredients. - Q: What does a test plan document? β Features to test / not test, strategy, effort, schedule, and entry / suspension / exit criteria.
Black-box - Q: What's hard about black-box testing? β The input space is huge/infinite with no code to guide you; the skill is choosing a small input set that still finds most faults. - Q: ECP for a 1β100 field? β 3 tests: one invalid-low (0), one valid (50), one invalid-high (101). - Q: Robustness testing = ? β BVA plus values just outside the range (minβ1, max+1) to check graceful error handling. - Q: Why pairwise instead of all combinations? β Most faults are β€2-way interactions, so covering all pairs catches most bugs at a fraction of the cost (27 combos β 9 tests). - Q: What's a t-way interaction fault? β A bug that only appears when t specific parameters take specific values together (e.g., Safari+Linux+Team = a 3-way fault).
White-box - Q: 100% statement coverage β is the code bug-free? β No. It only means every line ran, not that outputs were checked or every branch/combination was exercised. - Q: Statement vs branch coverage? β Statement = every line runs; branch = every decision goes both true and false. Branch subsumes statement. - Q: What is MC/DC and why is it used? β Each atomic condition independently flips the decision outcome; ~N+1 tests; the practical middle ground, mandated in avionics (DO-178C Level A). - Q: Advantage/disadvantage of multiple-condition coverage? β Advantage: most thorough (all condition combinations). Disadvantage: exponential β 2βΏ tests. - Q: What is cyclomatic complexity and what does it tell you? β V(G) = EβN+2P = predicates+1; it's the number of independent basis paths (min tests for branch coverage) and a complexity/refactor signal (>10 = risky). - Q: What does data-flow testing target? β Def-use pairs β the paths from where a variable is set to where it's used (catches use-before-set, dead assignments). - Q: What does mutation testing measure, and its main problem? β The fault-detecting power of your tests (mutation score = mutants killed Γ· total); main problem = equivalent mutants (functionally identical, can't be killed).
Integration / System / Maintenance - Q: What does integration testing target? β Interface faults between modules (wrong data/format/order), not logic inside a module. - Q: Stub vs driver? β Stub replaces a called module (top-down); driver replaces a calling module (bottom-up). - Q: Alpha vs Beta? β Alpha = in-house pre-release; Beta = real users in the real environment pre-release. - Q: Why is error seeding rarely used in 2026? β Superseded by mutation testing (automated, more rigorous) plus coverage, static analysis, and code review. - Q: Which tests do you automate vs skip? β Automate stable/repetitive/high-value (regression, smoke, API, data-driven); keep manual the exploratory, usability, one-off, and rapidly-changing-UI tests.
OO testing - Q: What's the suitable unit for testing an OO program, and why? β The class β because methods interact through shared object state, so you must test method sequences, not isolated methods. - Q: Should inherited methods be retested? β Yes, in the subclass's context, when the subclass overrides methods they call or changes state they depend on (the "yo-yo problem"). - Q: Why is dynamic binding hard to test? β The actual method run is decided at runtime, so one call site can invoke many implementations β you must test every possible binding. - Q: What is state-based testing? β Model the object as a state machine and test that events cause the right transitions and that invalid transitions are rejected.
Quick memory sheet (say these fast)¶
- Fault model = the kinds of bugs you expect; every technique targets one.
- Coverage-based = "did we execute it?"; fault-based (mutation) = "would we catch a bug?"
- Test case = recipe; test data = ingredients.
- ECP = 1 valid + 2 invalid classes; BVA = the edges; Robustness = edges + just outside.
- Decision table = all rule combinations (2βΏ); Pairwise/t-way = cover all pairs/t-tuples, not all combos.
- Coverage hierarchy: Statement < Branch < Condition/Decision < MC/DC < Multiple-Condition. (Path coverage is a separate, very strong criterion β don't tack it on the end of this chain.)
- MC/DC = each condition independently flips the outcome; ~N+1 tests; required in avionics (DO-178C).
- Cyclomatic complexity V(G) = predicates + 1 = # basis-path tests.
- Data-flow = def-use pairs; Mutation = kill injected faults to measure test power.
- Integration targets interface faults: top-down (stubs) / bottom-up (drivers) / sandwich.
- Alpha = in-house; Beta = real users pre-release; Acceptance = customer sign-off.
- Error seeding = estimate remaining bugs (
rΓS/s); modern successor = mutation testing. - OO unit = the class (methods share state); re-test inherited methods when the subclass overrides/adds state they depend on; dynamic binding β test every binding; use state-based testing.