Software Engineering introduced testing at a high level; this subject goes deep into how to design tests that find bugs efficiently. The JNTUK course follows a structured, model-based approach: model the program as a graph, then choose test paths that cover it. These ideas are what tools such as JaCoCo, coverage reports, pytest and SonarQube measure. This article revisits the five units and links each to current practice.
Unit 1: Fundamentals of testing and the taxonomy of bugs
Purpose of testing
The terms are precise:
| Term | Meaning |
|---|---|
| Error (mistake) | A human action that produces a wrong result |
| Fault (bug, defect) | The incorrect code or design that results |
| Failure | The wrong behaviour seen when the fault runs |
| Testing | Running a program to find failures |
| Debugging | Finding and fixing the fault behind a failure |
Testing can show the presence of bugs, never their absence. Its goals are to find bugs, build confidence and prevent defects (writing tests early finds requirement and design problems). Dichotomies the syllabus contrasts: testing versus debugging, functional versus structural, designer versus tester, modularity versus efficiency, programming in the small versus in the large, builder versus buyer.
The pesticide paradox and other principles
- Exhaustive testing is impossible, so tests are chosen by risk.
- Pesticide paradox: the same tests stop finding new bugs, so test sets must be refreshed.
- Defects cluster: a small share of modules holds most bugs.
- Early testing saves cost; fixing a bug in production is far dearer than in design.
- Testing is context-dependent: a game and a payment system need different testing.
Bug taxonomy
| Category | Examples |
|---|---|
| Requirements and specification | Missing, ambiguous or contradictory requirements |
| Structural | Control-flow errors: unreachable code, wrong loop conditions, missing cases |
| Data | Wrong initialisation, type mismatch, bad array bounds |
| Coding | Typos, wrong operators, off-by-one |
| Interface and integration | Wrong parameters, protocol mismatches |
| System and architecture | Race conditions, resource leaks, timeouts |
| Test and test-design bugs | The test itself is wrong |
A model of testing views the program as an environment, a program and a tester, with models of each.
Today: the “defects cluster” principle is why teams track hot-spot files, and the pesticide paradox is why test suites need maintenance. The error, fault and failure terms are also the vocabulary of incident reviews: a typo (error) causes a bug (fault) that appears as an outage (failure).
Unit 2: Flow graphs and path testing
Control flow graphs
A control-flow graph models a program: nodes are statements or blocks, edges are possible jumps. Decisions create branches, loops create cycles. A path is a sequence from entry to exit.
Path testing
Path testing designs tests so that selected paths through the graph are executed. Levels of coverage, from weakest to strongest:
| Criterion | Requirement |
|---|---|
| Statement coverage (C0) | Every statement runs at least once |
| Branch (decision) coverage (C1) | Every decision takes both true and false |
| Condition coverage | Every condition in a decision is both true and false |
| Multiple condition coverage | All combinations of conditions |
| Path coverage | Every possible path (often infinite with loops) |
Statement coverage is not enough: a test can run every line yet never take the false side of an if. A standard compromise is basis-path testing.
Cyclomatic complexity
The number of independent paths is the cyclomatic complexity:
V(G) = E − N + 2 (edges minus nodes plus 2), or number of decisions + 1.
A function with three if statements has V(G) = 4, so at least four tests are needed for basis-path coverage. High values (above about 10) mean code that is hard to test and maintain.
Loops and predicates
Loops are tested with the cases: skip the loop, one pass, two passes, a typical number, maximum and maximum ± 1. Predicates are the conditions in decisions; testing them also considers path predicates (what input makes each path execute), path sensitization (finding that input) and achievable versus unachievable paths.
Today: coverage tools (JaCoCo, coverage.py, Istanbul, Codecov) report statement and branch coverage on each pull request. SonarQube reports cyclomatic complexity. A “100% coverage” badge still does not prove correctness: it shows lines ran, not that the results were checked.
Unit 3: Transaction-flow, data-flow and dataflow testing
Transaction flow testing
A transaction here is a unit of work with a beginning, steps and an end (a customer order, a bank transfer). A transaction flow graph models its path through the system, including forks, joins, births (a new transaction) and deaths. Testing makes sure each path through the flow works, which finds problems in the high-level flow rather than in a single function.
Data-flow testing
Data-flow testing follows variables through a program. Each use of a variable has a role:
| Term | Meaning |
|---|---|
| def | The variable is given a value |
| c-use | Used in a computation |
| p-use | Used in a predicate (a decision) |
| kill | The variable is destroyed or undefined |
Anomalies to catch: define then define again without use (dd), use before define (ud, a bug), define then never use (du, wasted work). Coverage criteria include all-defs, all-c-uses, all-p-uses and all-du-paths: for each definition, test paths to its uses.
Data-flow anomaly detection
Static analysers find anomalies without running the code, as compilers do for “variable may be uninitialised” or “unused variable”.
Slicing
A program slice is the part of the code that affects a variable at a given point. Slices help debugging (what can influence this wrong value?) and testing.
Today: compilers, IDEs and linters (ESLint, Pylint, SonarQube, SpotBugs) apply data-flow analysis to flag uninitialised variables, dead stores and unused code. Security scanners use taint analysis (tracking data from user input to dangerous operations) which is data-flow testing for vulnerabilities such as SQL injection.
Unit 4: Domain testing and logic-based testing
Domain testing
Many bugs occur at the boundaries of input ranges. Domain testing treats the input space as domains separated by boundaries and tests near them.
| Technique | Idea |
|---|---|
| Equivalence partitioning | Divide inputs into classes expected to behave the same; test one from each |
| Boundary value analysis (BVA) | Test at, just below and just above each boundary |
| Robust BVA | Also test invalid values beyond the limits |
Example: a field accepts ages 18 to 60. Partitions: below 18 (invalid), 18 to 60 (valid), above 60 (invalid). Boundary tests: 17, 18, 19, 59, 60 and 61. Typical bugs: < written as <=, an off-by-one limit. Domain closure, nice and ugly domains, and interior and extreme points are also covered.
Logic-based testing
When behaviour depends on many conditions, use a decision table: conditions in rows, rule combinations in columns, actions at the bottom. It makes missing or contradictory rules visible. A cause-effect graph turns the specification into a logic graph and then into a decision table. KV charts (Karnaugh-Veitch maps) help simplify the Boolean logic.
| Concept | Meaning |
|---|---|
| Boolean algebra of conditions | Express the decision logic; simplify |
| MC/DC | Modified condition/decision coverage: each condition independently affects the decision’s outcome; required in aviation software |
| Predicate testing | Find faults in the Boolean expressions themselves |
Today: input validation tests (age, quantity, string length, date ranges) always use equivalence classes and boundaries. Property-based tools (Hypothesis, QuickCheck) generate many values automatically. Decision tables model pricing rules, feature-flag combinations and permission matrices. Safety-critical standards (DO-178C, ISO 26262) require MC/DC.
Unit 5: State testing, graph matrices and tools
State, state graphs and state testing
Many systems behave according to a state (an order is Created, Paid, Shipped; a connection is Open or Closed). A state graph shows states, transitions triggered by inputs and the outputs.
| Coverage level | Requirement |
|---|---|
| State coverage | Every state visited |
| Transition coverage | Every transition taken |
| Transition-pair / N-switch coverage | Every sequence of N transitions |
Tests also look for unreachable states, missing transitions (what happens on an input not handled?), ambiguous or conflicting transitions, and equivalent states. A state table (state × input) makes gaps obvious.
Graph matrices and node reduction
A graph matrix records a graph as a table; powers of the matrix give paths of a given length. It supports node-reduction algorithms for path expressions and connection analysis. It is the algebra behind covering all paths in a graph.
Levels and kinds of testing (the practical view)
| Level or kind | Focus |
|---|---|
| Unit | One function or class, with mocks for dependencies |
| Integration | Modules and services together; contract tests for APIs |
| System / end-to-end | The whole product; UI and API flows |
| Regression | Re-run after changes |
| Performance and load | Speed and capacity under stress |
| Security, usability, compatibility | Specialist concerns |
| Acceptance | The customer’s check |
The test pyramid advises many fast unit tests, fewer integration tests and few slow end-to-end tests.
Test automation and tools
| Need | Tools |
|---|---|
| Unit tests | JUnit, pytest, Jest, Go’s testing |
| UI and end-to-end | Selenium, Cypress, Playwright |
| API tests | Postman, REST Assured |
| Performance | JMeter, k6, Locust |
| Code quality and coverage | SonarQube, JaCoCo |
| Mutation testing | PIT, Stryker (checks that your tests detect injected bugs) |
| Test management | Jira, TestRail |
Today: state-machine testing covers order workflows, retry logic and Kubernetes controllers. A CI pipeline runs the test pyramid on every commit: lint, unit tests, integration tests with containers, then end-to-end and load tests before release, with a quality gate in SonarQube that blocks a merge if coverage or complexity falls below a threshold.
One-minute recap
| Unit | Key idea | You meet it in |
|---|---|---|
| 1. Fundamentals and bugs | Error, fault, failure; pesticide paradox; bug taxonomy | Incident reviews, hot-spot analysis |
| 2. Flow graphs and path testing | Statement, branch, path coverage; V(G) = E − N + 2 | Coverage reports, SonarQube complexity |
| 3. Transaction flow and data flow | def/use anomalies, slicing | Linters, taint analysis for security |
| 4. Domain and logic testing | Equivalence classes, boundary values, decision tables, MC/DC | Input validation tests, property-based tests |
| 5. State testing and tools | State and transition coverage; test pyramid; automation | CI test stages, Selenium and Playwright, k6 |
Next in Engineering Revisited: Unix Programming.
Written by Claude, Anthropic’s AI assistant, as part of Engineering Revisited, a quick refresher of the B.Tech subjects one at a time.