← Back to all posts

Software Testing: A Practitioner's Walkthrough

• By Kevin Sullivan
software-testingqatest-automationregression-testingfundamentals

Software Testing: A Practitioner's Walkthrough

Testing is often taught as a checklist activity bolted onto the end of development. It isn't. It's a discipline with its own theory, its own techniques, and its own economics, and it rewards the same curiosity you'd bring to any hard engineering problem. This post walks through the core ideas of software testing from first principles to process, organised the way I'd teach it: start with what testing is, build the structural scaffold of testing levels, then layer in the techniques, the specialised domains, the automation, and finally the estimation that turns all of it into a plan you can defend.

1. What testing actually is (and isn't)

A few principles set the frame for everything else.

Testing is a craft, not a recipe. The methods haven't changed dramatically in decades, and no tool makes a tester redundant. Good testing leans on creativity, experience, and intuition paired with proper technique. The technique without the judgement gives you brittle, low-value tests; the judgement without technique gives you ad-hoc poking that misses whole categories of defect.

Testing is more than debugging. Debugging is the narrow act of locating an error and correcting it. Testing is the broader activity: it covers validation, verification, and reliability measurement. You're not just hunting for the bug in front of you — you're building evidence about whether the system does the right thing and how dependable it is.

This is where Verification and Validation (V&V) earns its place. The two words are not synonyms:

  • Verification asks "are we building the product right?" — does the software conform to its specification?
  • Validation asks "are we building the right product?" — does it actually meet the user's real need?

V&V is one of several methodologies for producing quality software, and it matters because organisations now depend on software to manufacture products, deliver services, run daily operations, and support management decisions. That dependence is exactly why reliability and security stopped being optional.

Testing is expensive. It's one of the most laborious phases of the software process, which is the whole motivation for automation — done well, it cuts cost and time. Coverage-based techniques are judged on efficiency and effectiveness for precisely this reason.

Complete testing is infeasible. You cannot exhaustively test a non-trivial system; the input and path combinations explode. Complexity is the root of the problem, and accepting that is liberating rather than defeating — it's why we need techniques that select high-value test cases instead of pretending to test everything.

Testing is not always the best lever for quality. Catching defects late is the most expensive place to catch them. Better design, better process, and prevention often move the quality needle more than adding test cases ever will.

2. The levels of testing: the structural scaffold

Almost everything else hangs off this progression. Testing moves outward in scope:

  1. Unit / module testing — the smallest testable pieces in isolation.
  2. Integration testing — units combined, with focus on the interfaces between them.
  3. System testing — the whole assembled system against its requirements.
  4. Acceptance testing — does it satisfy the customer? This splits into alpha (internal, in-house) and beta (external, real users).
  5. Regression testing — performed during maintenance, every time something changes.

System testing is not one test but n tests. In practice it tends to include:

  • End-to-end integration testing
  • User interface testing
  • Load testing, measured along several axes: volume/size, number of simultaneous users, and transactions per minute or second (TPM/TPS)
  • Stress testing — pushing past expected limits to see how the system fails
  • Availability testing — sustained 24×7 operation

Performance testing deserves a specific warning: it's conceptually easy to understand but genuinely hard to execute, because it demands a lot of information and a lot of setup effort to produce results you can trust.

3. Black-box techniques: testing behaviour, not internals

Black-box testing treats the system as opaque — you reason from inputs and expected outputs without looking at the code. The skill is choosing the right technique for the scenario. Here's the map:

When the scenario involves...Reach for...
Outputs dictated by combinations of input conditionsDecision tables
Input values in ranges, each range behaving differentlyBoundary Value Analysis (BVA)
Input values that fall into distinct classesEquivalence partitioning
Checking both expected and unexpected inputsPositive and negative testing
Workflows, process flows, or language processorsGraph-based testing
Confirming requirements are tested and metRequirements-based testing
Testing domain expertise rather than the written specDomain testing
Confirming documentation matches the productDocumentation testing

Two of these carry most of the weight in day-to-day work:

  • Boundary Value Analysis exists because bugs cluster at edges. If a field accepts 1–100, the interesting values are 0, 1, 100, and 101 — not 50. Off-by-one errors live precisely at these boundaries.
  • Equivalence partitioning is how you tame the combinatorial explosion. Group inputs that the system should treat identically, then test one representative from each class. If "any negative number" is handled by one branch, you don't need a thousand negative test cases — you need one good one, plus its boundary.

Decision tables shine when output depends on a matrix of conditions, because they force you to enumerate combinations you'd otherwise forget. Graph-based testing is the natural fit for flows and state — anything you'd naturally sketch as nodes and edges.

4. White-box techniques: testing with the code open

White-box testing uses knowledge of the internal structure. Its concerns are the ones invisible from the outside:

  • Memory leaks
  • Uninitialised memory
  • Garbage collection issues (a particular concern in managed runtimes like Java)

The book lists period-specific tooling — Purify and Quantify (Rational), Insure++ (Parasoft), Expeditor (OneRealm). The tools have changed, but the category hasn't: dynamic analysis and memory profilers that instrument your binary and report what static reading of the source can't reveal. The modern equivalents are sanitisers, profilers, and leak detectors, but the intent is identical.

5. Gray-box testing and the tester's knowledge

Gray-box sits between the two: partial knowledge of internals, used to design smarter behavioural tests. The underlying point is about the tester, not the colour of the box.

Effective testers pull ideas from a wide range of knowledge areas, because testing is far more effective when you know what kinds of bugs you're hunting. The goal is a broad enough understanding of the system to challenge it as hard as real-world use will. And at the team level, you don't need every tester to be a gray-box specialist — a mix of tester types and skill sets produces better results than a uniform team.

6. Testing across domains

Some contexts impose their own demands. Three worth calling out:

Object-oriented systems

OO breaks some procedural assumptions, so the testing adapts:

  • Object orientation itself means data and methods are bound more tightly, so tests must integrate the two rather than treating them separately.
  • Unit testing of classes combines BVA and equivalence partitioning for the variables, code-coverage methods for the class's methods, state diagrams to exercise object states, and stress testing to surface memory leaks.
  • Abstract classes must be retested for every concrete implementation.
  • Inheritance introduces extra context — each combination of inherited contexts has to be tested, and encapsulation calls for incremental class-level testing.
  • Polymorphism means each same-named method must be tested separately; convenient for the writer, harder for the maintainer.
  • Dynamic binding breaks conventional coverage assumptions and raises the chance of unanticipated runtime defects, so coverage criteria have to be adapted.
  • Inter-object communication is tested via message sequencing and sequence diagrams.
  • Reuse and parallel development blur the line between unit and integration testing, demand more frequent integration and regression runs, and make interface errors more likely — so interfaces need thorough testing.

Web applications

A website fails the user if either dimension is neglected, so web testing combines functional and non-functional methods, covering performance, security, reliability, and user interface. For anyone wearing a security hat, this is the natural home for the non-functional concerns that don't show up in a feature spec.

Databases

Database testing includes regression testing of relational databases and black-box testing of database code — verifying that schema changes, stored logic, and queries continue to behave as data and structure evolve.

7. Regression testing: defending what already works

Regression testing confirms three things at once: that fixed bugs stayed fixed, that the fixes didn't introduce new bugs, and that features previously proven correct are still intact. It's the safety net under every change.

Cadence depends on project size — a full regression pass might run once per milestone or once per build, with lighter bug-regression cycles during acceptance testing focused only on the most important defects. Crucially, regression tests are highly automatable, which is exactly why they're the first place an automation investment pays off: they're repetitive, frequent, and unforgiving of human boredom.

8. Automation and the testing workbench

Because testing is expensive and laborious, testing tools were among the earliest software tools built. A mature testing workbench integrates several of them:

  • Test manager — runs the tests and tracks test data, expected results, and what's been exercised.
  • Test data generator — produces input data, sometimes by selecting from a database.
  • Oracle — generates the predictions of expected results to compare against.
  • File comparator — diffs current results against previous runs and reports the differences.
  • Report generator — defines and produces result reports.
  • Dynamic analyzer — instruments the program to count how often each statement executes, yielding a coverage/execution profile.

Two honest caveats come with this. First, a workbench almost always has to be adapted to each system's test suite, and building a comprehensive one takes real effort and time — most are open systems because testing needs are organisation-specific. Second, automation is not a silver bullet. What it genuinely buys you is reproducibility, better coverage, reduced manual effort, and — maybe most valuable — quantifiable metrics that let you describe the health of a product in numbers rather than vibes.

9. Estimating a testing effort: Test Point Analysis

Estimation is one of the hardest and most consequential activities in IT, because when you commit to a cost and a date, you're held to it. The book tells a useful cautionary story.

The naive model computed testers as half the developer headcount and testing days as a third of the development days:

Testing working days = Development working days / 3
Testing engineers     = Development engineers / 2
Testing cost          = Testing days × Testing engineers × Person daily cost

It was simple and it "worked" for years — but it was just arithmetic on developer numbers, ignoring requirements, metrics, analogies, and expertise. Nobody on the test team believed in it, and neither did the developers.

The replacement was a set of rules that ground estimation in evidence rather than ratios:

  1. Base it on requirements — you estimate what will actually be tested, and the test team must read and understand the requirements too.
  2. Base it on expert judgement — classify each requirement as Critical, High, or Normal based on how well the team knows how to implement it, then let experts estimate effort accordingly.
  3. Base it on previous projects — reuse estimates from similar past work.
  4. Base it on metrics — drawn from an Organisation Process Database (OPD) accumulated over years and dozens of projects.
  5. Never discard the past — keep running the old model alongside the new one to compare; a large divergence is a signal to re-check the new estimate.
  6. Record every decision — so that when requirements change, you re-estimate from the records instead of redoing everything.
  7. Support it with tools — a spreadsheet computes cost and duration per phase, and a customer-facing letter template captures cost tables, risks, and options.
  8. Always verify — compare each new estimate against recorded ones for a consistent trend; deviation triggers a re-estimate.

The metric-driven core uses numbers like test cases per requirement, test cases developed per working day, test cases executed per working day, anomaly reports (ARs) per test case, and ARs verified per working day. Feed in a requirement count and a team size, and you get a defensible breakdown across preparation, execution, and regression phases.

This is Test Point Analysis (TPA): a way to quantify effort even for black-box testing, where you have no code metrics to lean on. Use-case analysis can serve a similar role. The deeper lesson is that "how long will testing take?" deserves a method, not a guess.

10. The bottom line: quality has trade-offs

A disciplined approach to testing delivers real advantages — a product that meets its quality requirements, built within time and budget, with the predictability that comes from process discipline, and ultimately happier customers.

But the trade-offs are just as real. Quality is sometimes compromised to hit a deadline or a budget. Thorough testing is time-consuming. And doing it well needs people, which adds cost. None of these are reasons to skip testing; they're the constraints you're managing while you test. The job isn't to eliminate the tension between quality, time, and cost — it's to make those trade-offs deliberately and with evidence, rather than by accident.


The one-paragraph takeaway

Testing is a craft built on a small number of hard truths: you can't test everything, late defects are the costly ones, and verification and validation are different questions. Around those truths sits a toolkit — levels that scope your effort from unit to acceptance, black-box techniques for choosing high-value cases from infinite possibilities, white-box techniques for the defects only the code reveals, automation for the repetitive work, and estimation methods like TPA for turning all of it into a plan you can stand behind. Master the judgement of which technique fits which scenario, and the tools become details.