6. Verification, Validation and Software TestingIntroduction and ContextSoftware systems fail for many reasons: a requirement may be misunderstood, an algorithm may be incorrectly implemented, an interface may behave unpredictably under load, or an operational assumption may not hold in practice. Verification, validation, and testing provide disciplined ways of discovering and reducing these failures before and after deployment. They are not merely final inspection activities. They influence requirements, architecture, design, coding, deployment, maintenance, and organizational decision-making. Verification asks whether a work product has been built correctly according to specified properties. Validation asks whether the resulting system solves the intended user and business problem. Testing supplies evidence about software behaviour by executing the system or examining it without execution. Since exhaustive testing is generally impossible for non-trivial systems, professional testing is a risk-based activity: effort is directed toward failures that are most likely, most damaging, or most difficult to detect later. For postgraduate software engineering students, the central issue is not memorizing testing levels. It is designing an evidence-producing quality strategy that connects requirements, risks, architecture, test techniques, automation, metrics, and release decisions. Learning ObjectivesAfter studying this chapter, students should be able to: 1. Distinguish verification, validation, testing, debugging, and quality assurance. 2. Construct a risk-based test strategy for complex software systems. 3. Select and relate unit, integration, system, acceptance, regression, performance, usability, and security testing. 4. Apply static analysis, coverage analysis, test automation, and formal verification appropriately. 5. Interpret test results and metrics without confusing measurement with quality. 6. Evaluate professional, ethical, managerial, and governance issues in software testing. 7. Design a practical testing approach for a safety-critical or business-critical scenario. Verification, Validation, and the Evidence of QualityVerification is concerned with conformance to stated specifications. Reviews of requirements, architectural models, interface contracts, source code, and test designs are verification activities. A compiler check, a type checker, and a static analyzer also provide forms of verification. The question is, “Have we built the product right?” Validation is concerned with fitness for intended use. User acceptance testing, usability evaluation, operational pilots, and domain-expert assessment are validation activities. They address the question, “Have we built the right product?” A system can be verified against an inaccurate requirement and still fail validation. Testing is a systematic examination of software behaviour or artefacts to find defects and provide information for decisions. Debugging is different: it is the investigation and correction of the cause of an observed failure. Testing reveals that a problem exists; debugging localizes and removes its cause. Quality assurance is broader still, encompassing process definition, audits, improvement, standards, and prevention of defects. Quality control focuses more directly on evaluating deliverables. A useful distinction is between an error, a defect, and a failure. A human mistake creates an erroneous design decision or program statement. The resulting flaw in a work product is a defect. When execution produces behaviour that violates an expected result, a failure has occurred. A latent defect may never cause a failure under the tested conditions, which explains why passing tests cannot prove the absence of defects. Testing also has an oracle problem: the expected result is not always obvious or easy to compute. Test oracles may be requirements, mathematical properties, reference implementations, invariants, metamorphic relations, domain rules, or human evaluation. In scientific and machine-learning software, metamorphic testing is valuable when exact outputs are difficult to predict. For example, translating an image or changing the order of independent records should not alter a classification or aggregate result in ways prohibited by the specification. A Risk-Based Testing StrategyA test strategy describes the purpose, scope, priorities, environments, responsibilities, techniques, tools, and decision rules for testing. A risk-based strategy begins with business and technical risks rather than with a predetermined list of test levels. Risk is often assessed as a combination of probability and impact. Probability may reflect technical complexity, change frequency, novelty, dependency uncertainty, or historical defect patterns. Impact may involve financial loss, safety, privacy, legal exposure, reputation, service availability, or harm to users. A simple risk priority score can support discussion, but it should not replace professional judgment. A rare safety failure may deserve more attention than a frequent but harmless display defect. The strategy normally proceeds through the following reasoning cycle: - identify quality risks and affected stakeholders;
- trace risks to requirements, architecture, components, interfaces, and operational scenarios;
- choose test levels and techniques capable of detecting each risk;
- define environments, data, tools, independence, and evidence requirements;
- establish entry criteria, exit criteria, defect severity rules, and escalation paths;
- execute, analyze, report, and revise testing as evidence changes.
Traceability links requirements to risks, tests, and defects. It supports impact analysis and regulated release decisions, but traceability alone does not guarantee meaningful coverage. A hundred tests linked to a requirement may still exercise only ordinary cases. The test pyramid is a useful heuristic. A broad base of fast unit tests is supported by fewer service or component integration tests and a smaller number of slower end-to-end tests. The pyramid is not a law: systems with complex interactions may need substantial contract, integration, exploratory, or infrastructure testing. Its main lesson is to place rapid, diagnostic checks at lower levels and reserve expensive system tests for behaviours that cannot be established elsewhere. Test Levels and Their RelationshipsUnit and Component TestingUnit tests examine a small, logically cohesive unit in isolation. They test algorithms, boundary conditions, error handling, state transitions, and local invariants. Test doubles—stubs, mocks, fakes, and spies—can isolate dependencies, although excessive mocking may test implementation details rather than useful behaviour. Good unit tests are deterministic, fast, focused, and readable. They should include equivalence classes, boundary values, invalid inputs, exceptional paths, and interaction sequences. Property-based testing complements example-based tests by generating many inputs and checking general properties such as conservation, idempotence, or ordering. Integration and Contract TestingIntegration testing examines interactions among modules, services, databases, operating-system facilities, networks, or external providers. Defects often arise at boundaries: incompatible data types, different assumptions about time zones, transaction semantics, retries, versioning, and error protocols. Contract testing specifies the obligations between a service provider and its consumer. It can detect interface incompatibility earlier and more cheaply than a complete end-to-end test. Integration environments should resemble production where relevant, but teams should distinguish infrastructure failures from software failures rather than masking both with unreliable test setups. System TestingSystem testing evaluates the integrated product against functional and non-functional requirements. It includes realistic workflows, configuration, deployment packaging, logging, recovery, authorization, compatibility, and interactions with external systems. System tests should include abnormal conditions such as network loss, resource exhaustion, duplicate messages, clock changes, and partial service failure when these are credible risks. Acceptance TestingAcceptance testing determines whether stakeholders can accept the system for operational use. User acceptance testing is usually business- or domain-led, while operational acceptance may assess backup, monitoring, support procedures, installation, and recovery. Acceptance criteria should be defined early; they should not be invented after implementation to justify a release. Regression TestingRegression testing checks that a change has not damaged existing behaviour. A regression suite should be selected using change impact, risk, defect history, and critical workflows—not simply by running every test indiscriminately. Automated smoke tests can provide rapid feedback after builds, while deeper suites run at scheduled pipeline stages. Failed tests must be triaged: a test may expose a product defect, an obsolete expectation, test-data corruption, or environment instability. Static Analysis and Formal VerificationStatic testing examines artefacts without executing the program. Requirements inspections, design reviews, walkthroughs, code review, linting, type checking, dependency analysis, and static application security testing can expose defects early. Reviews are particularly effective for ambiguous requirements, unsafe assumptions, interface contracts, and maintainability concerns. Static analyzers can identify unreachable code, null dereferences, data-flow anomalies, insecure APIs, duplication, complexity, and certain concurrency problems. Their warnings require interpretation. Excessive false positives lead to alert fatigue, while overly permissive configurations conceal real risks. Teams should establish severity thresholds, suppression justification, ownership, and periodic rule review. Formal verification uses mathematical models to demonstrate that specified properties hold under defined assumptions. Model checking explores state spaces for violations of temporal or logical properties. Theorem proving can establish correctness of algorithms or implementations. Formal methods are especially valuable for protocols, compilers, cryptographic components, control systems, and safety-critical logic. They are not a universal substitute for testing: the model may omit relevant reality, the specification may be wrong, and usability or performance properties may remain empirical questions. Designing Effective Test CasesA test case should state its objective, preconditions, inputs, actions, expected results, data dependencies, and evidence to be retained. Test design techniques include: - equivalence partitioning, which selects representative values from behaviourally similar classes;
- boundary-value analysis, which emphasizes limits where errors commonly occur;
- decision-table testing for combinations of conditions and business rules;
- state-transition testing for event-driven or lifecycle-based systems;
- pairwise or combinatorial testing when many configuration variables interact;
- use-case and scenario testing for user-visible workflows;
- exploratory testing, in which learning, test design, and execution occur together.
Negative testing is essential. Systems must be examined with malformed input, missing permissions, expired credentials, duplicate requests, unexpected sequence, invalid state, and resource constraints. Random input generation and fuzzing are useful for parsers, APIs, file formats, and protocol handlers, but generated inputs need meaningful monitoring and failure classification. Performance, Usability, Security, and ReliabilityPerformance testing investigates response time, throughput, resource utilization, scalability, and stability. Load testing applies expected demand; stress testing exceeds expected demand to expose limits; spike testing examines abrupt demand changes; endurance testing detects degradation over time; and capacity testing helps identify sustainable operating limits. Results must be interpreted with workload models, environment configuration, percentile latency, error rate, and saturation indicators. An average response time can conceal unacceptable tail latency. Usability testing observes representative users performing representative tasks. It can reveal discoverability problems, confusing terminology, inaccessible controls, excessive cognitive load, and unsafe workflows. Usability is not reducible to a defect count; qualitative observation and task success measures both matter. Accessibility testing should include automated checks and evaluation with appropriate assistive technologies and users. Security testing includes threat-informed functional tests, abuse-case testing, vulnerability scanning, penetration testing, authentication and authorization checks, input validation, secret management, and resilience against misuse. Security testing should be continuous and should not be postponed until the release candidate. Privacy tests should verify collection limitation, access control, retention, masking, and deletion behaviour where applicable. Reliability testing examines failure frequency, recovery, fault tolerance, and resilience. Fault injection, chaos experiments, backup restoration, failover tests, and disaster-recovery exercises are valuable when conducted within controlled safety boundaries. A system that survives a component failure but loses data during recovery has not met its reliability objective. Automation, Continuous Integration, and CoverageTest automation provides repeatability, speed, and rapid feedback, particularly for regression and continuously delivered software. Automation is not simply the conversion of manual scripts into code. Automated tests require maintainable fixtures, stable interfaces, meaningful assertions, diagnostic logs, data management, and ownership. Brittle tests increase delivery risk and may cause teams to ignore failures. Continuous integration executes builds, static checks, unit tests, and selected integration tests whenever changes are submitted. Continuous delivery adds deployment automation and progressive validation in realistic environments. Parallel execution, isolated test data, containerized dependencies, service virtualization, and risk-based pipeline stages can reduce feedback time. Coverage measures describe what has been exercised, not what has been proved correct. Statement coverage records executed statements; branch coverage records decision outcomes; condition coverage examines Boolean subexpressions; path coverage considers execution paths and is often impractical for complex programs. Requirements coverage and risk coverage provide complementary views. High code coverage can coexist with weak assertions, untested data combinations, or incorrect expected results. Coverage is therefore a diagnostic signal and conversation starter, not a quality certificate. Mutation testing evaluates test effectiveness by introducing small artificial changes, or mutants, into code and observing whether tests detect them. A high mutation score may reveal stronger assertion quality than line coverage alone, although mutation testing can be computationally expensive. Interpreting Results and Making Release DecisionsA test report should communicate tested scope, untested scope, environment, data, failures, severity, trends, residual risks, and recommendation. “All tests passed” is meaningful only when the relevant tests, assumptions, and environment are understood. A release decision should consider unresolved defects, risk acceptance, operational readiness, monitoring, rollback capability, and stakeholder authorization. Useful metrics include defect discovery and escape rates, mean time to detect and repair, flaky-test rate, test execution time, risk coverage, change failure rate, and recovery time. Metrics can distort behaviour when treated as targets. For example, rewarding teams for closing defects may encourage reclassification; rewarding test count may produce many low-value tests. Metrics should support inquiry and learning rather than blame. Illustrative Case Study: Testing a Digital Payment ServiceBackgroundA fictional financial technology company is preparing a service that authorizes mobile payments, records transactions, and communicates with banks and fraud-detection providers. The service must remain available during peak periods and must not authorize the same payment twice. Situation and Risk AnalysisThe principal risks are duplicate charging, incorrect authorization, loss of transaction records, exposure of personal data, provider timeout, and unacceptable latency during demand spikes. Duplicate charging has high impact even if its probability is moderate; a minor visual defect has lower priority. Strategy and ApproachThe team creates traceability from payment rules and service-level objectives to tests. Unit tests cover currency calculations, idempotency keys, authorization rules, and state transitions. Contract tests verify message schemas with banking and fraud services. Integration tests exercise transaction boundaries, retries, timeouts, and reconciliation. System tests run realistic payment journeys, including interrupted network connections and repeated submissions. Performance tests model normal and peak demand, measuring percentile latency, throughput, error rate, and database contention. Security tests examine authentication, authorization, replay, logging of sensitive data, and abuse of transaction identifiers. Static analysis checks dependency vulnerabilities, data-flow issues, and unsafe API use. A controlled fault-injection exercise stops the fraud provider and verifies that the documented fallback policy is followed. Acceptance testing is performed by operations and finance representatives using controlled accounts. The release pipeline runs unit and static checks for every change, contract and integration tests for merge validation, and a selected system and performance suite before production deployment. Production rollout is gradual, with monitoring and rollback criteria. OutcomeTesting reveals that a retry mechanism creates a second transaction when the first response is lost. The defect is corrected by enforcing idempotency at the persistent transaction boundary rather than only in the API layer. A performance test also shows that average latency is acceptable while the ninety-ninth percentile is not; indexing and connection-pool changes improve tail behaviour. The service is released only after unresolved medium-risk issues are explicitly accepted by authorized stakeholders and operational recovery is demonstrated. Learning PointsThe case illustrates that risk analysis determines test priorities, integration boundaries deserve special attention, performance requires distributional measures, and release confidence depends on evidence from multiple techniques. No single automation suite could establish payment correctness, security, usability, and operational readiness. Challenges, Ethics, and Professional GovernanceTesting is affected by incomplete requirements, limited environments, unstable data, unrealistic workloads, nondeterministic concurrency, legacy code, third-party dependencies, and pressure to meet release dates. Common mistakes include testing only normal flows, equating coverage with correctness, ignoring non-functional requirements, allowing developers to conceal failures, and treating flaky tests as harmless. Testers and engineers have a professional duty to report material risks honestly. Suppressing a safety or privacy defect, manipulating test data, or presenting unrepresentative performance results can harm users and violate organizational governance. Sensitive production data should not be copied casually into test environments; masking, synthetic data, access controls, retention limits, and audit trails are required. Regulated domains may require documented independence, validated tools, electronic records, traceability, and evidence that changes were controlled. Independence improves the likelihood that failures will be reported, but complete separation is not always efficient. Developers should test their code, while independent testers, domain experts, and operations personnel contribute different perspectives. Psychological safety is essential: defects are information about the system and process, not grounds for personal blame. Emerging DirectionsModern development is increasing the use of model-based testing, property-based testing, continuous testing in delivery pipelines, service virtualization, observability-driven validation, and AI-assisted test generation. Large language models can suggest test cases, summarize failures, generate data, or identify likely boundary conditions, but their output requires human review. They may reproduce incorrect assumptions, miss security implications, or generate tests that assert implementation rather than requirements. Testing AI-enabled systems introduces additional concerns: data drift, nondeterministic outputs, bias, explainability, adversarial inputs, and changing model versions. Evaluation should include representative and challenging datasets, subgroup analysis, robustness checks, monitoring, and clearly defined human-override procedures. Digital twins and realistic simulation can extend testing for cyber-physical systems, while production telemetry and synthetic transactions support validation after deployment. Best-Practice PrinciplesA mature testing approach begins early, makes risks explicit, and treats requirements as testable claims. It combines static and dynamic techniques, layers tests for speed and diagnosis, automates repeatable regression checks, and preserves targeted human exploration. Tests should be deterministic where possible, isolated where appropriate, and maintained as production assets. Teams should measure meaningful coverage, investigate escapes, review residual risk, protect test data, and make release decisions transparent. Above all, testing should provide credible evidence—not false certainty. Chapter SummaryVerification determines whether artefacts conform to specified requirements, while validation determines whether the system is fit for intended use. Testing, reviews, static analysis, formal methods, and operational evaluation provide complementary evidence. A risk-based strategy connects business impact and technical probability to test levels, techniques, environments, and release decisions. Unit, integration, system, acceptance, regression, performance, usability, security, and reliability testing address different failure classes. Automation and continuous integration improve feedback, but coverage metrics do not prove correctness. Static analysis and formal verification can identify or prove selected properties under explicit assumptions. Effective professionals interpret results critically, protect privacy, report risks honestly, and combine automation with domain expertise and exploratory judgment. Key TermsVerification: Evaluation of whether a work product conforms to specified requirements. Validation: Evaluation of whether a system satisfies intended user and business needs. Testing: Systematic examination of software or artefacts to find defects and provide evidence. Debugging: Investigation and correction of the cause of an observed failure. Defect: A flaw in a software work product that may cause incorrect behaviour. Failure: Observable behaviour that violates an expected result. Test oracle: A source or rule used to determine whether an output is correct. Risk-based testing: Prioritizing test effort according to likelihood and impact of failure. Unit test: A test of a small, logically cohesive software unit. Integration testing: Testing interactions among components, services, or external systems. Contract testing: Testing that provider and consumer interfaces satisfy agreed obligations. System testing: Evaluation of an integrated system against functional and quality requirements. Acceptance testing: Testing used to determine readiness for stakeholder or operational acceptance. Regression testing: Testing intended to detect unintended effects of changes. Static analysis: Automated or manual examination without executing the program. Formal verification: Mathematical demonstration that specified properties hold under stated assumptions. Code coverage: A measurement of which code elements or paths were exercised by tests. Mutation testing: Evaluation of test effectiveness using deliberately modified program versions. Fuzz testing: Automated testing with numerous generated or malformed inputs. Flaky test: A test whose result varies without a relevant change in software or test intent. Review QuestionsShort Answer Questions1. Distinguish verification from validation and give one example of each. 2. Why is debugging not equivalent to testing? 3. What is the purpose of a test oracle? 4. Explain why high statement coverage does not guarantee software correctness. 5. Differentiate load testing from stress testing. Long Answer Questions1. Explain how to develop a risk-based test strategy for a complex software system. 2. Compare unit, integration, system, and acceptance testing in terms of purpose, scope, and evidence. 3. Discuss the strengths and limitations of static analysis and formal verification. 4. Explain how automation, continuous integration, test pyramids, and coverage analysis should be combined. 5. Discuss ethical and governance responsibilities in testing software that handles sensitive or safety-related information. Analytical / Application Questions1. A service has excellent unit coverage but frequently fails when an external provider times out. Design an improved testing strategy and justify the selected techniques. 2. A release has acceptable average response time but unacceptable ninety-ninth percentile latency. Analyze what additional evidence and actions are required before release. 3. Design a risk-based test plan for a medication-dispensing application, including failure risks, test levels, non-functional testing, automation, and acceptance evidence. 22. Multiple Choice Questions with AnswersQuestion 1Which statement best distinguishes validation from verification? A. Validation checks whether code compiles, while verification checks user satisfaction. B. Validation checks fitness for intended use, while verification checks conformance to specified requirements. C. Validation is performed only by developers, while verification is performed only by users. D. Validation is dynamic testing, while verification is always formal proof. Correct Answer: B. Validation checks fitness for intended use, while verification checks conformance to specified requirements. Question 2A payment request is repeated after the client loses the first response. Which testing concern is most directly involved? A. Idempotency and duplicate transaction handling B. Statement coverage C. Visual accessibility D. Source-code formatting Correct Answer: A. Idempotency and duplicate transaction handling Question 3Which test level primarily examines whether a service provider and its consumer obey agreed interface obligations? A. Unit testing B. Usability testing C. Contract testing D. Acceptance testing Correct Answer: C. Contract testing Question 4Why can high code coverage coexist with poor software quality? A. Coverage tools cannot execute any code. B. Coverage measures exercised code elements but not necessarily meaningful assertions or correct requirements. C. High coverage proves that all requirements are wrong. D. Coverage applies only to manual testing. Correct Answer: B. Coverage measures exercised code elements but not necessarily meaningful assertions or correct requirements. Question 5A team wants to discover whether tests detect small alterations to program logic. Which technique is most appropriate? A. Mutation testing B. Load testing C. Code inspection only D. Smoke deployment Correct Answer: A. Mutation testing Question 6Which result is most important when evaluating a system whose average response time is acceptable but users report occasional severe delays? A. The number of source files B. The ninety-ninth percentile latency and associated error and resource data C. The number of unit-test fixtures D. The percentage of comments in the code Correct Answer: B. The ninety-ninth percentile latency and associated error and resource data Question 7What is a principal purpose of static analysis? A. To examine software artefacts without executing the program B. To replace all acceptance testing C. To guarantee usability in every population D. To measure only network throughput Correct Answer: A. To examine software artefacts without executing the program Question 8Which approach best reflects a risk-based test strategy? A. Run every possible test equally often regardless of impact. B. Prioritize testing according to likelihood, impact, change, and detectability of failures. C. Test only features requested by developers. D. Select tests solely by their execution speed. Correct Answer: B. Prioritize testing according to likelihood, impact, change, and detectability of failures. Question 9A test passes intermittently without a relevant product change. What is the most accurate description? A. Formal proof B. Flaky test C. Acceptance criterion D. Mutation survivor Correct Answer: B. Flaky test Question 10Which practice is most appropriate when production data contains personal information and is needed for testing? A. Copy the complete database to every developer machine. B. Disable access controls temporarily. C. Use synthetic or properly masked data with controlled access and retention. D. Remove all test evidence after each run. Correct Answer: C. Use synthetic or properly masked data with controlled access and retention.
|