Testing and Software Quality EngineeringIntroductionModern web applications are distributed systems composed of browsers, servers, databases, third-party services, identity providers, queues, and deployment pipelines. A defect in any one layer can affect confidentiality, revenue, accessibility, reliability, or user trust. Testing is therefore not merely an activity performed before release; it is an engineering discipline that provides evidence about whether a system satisfies stated and unstated quality requirements. Software quality engineering (SQE) combines testing, prevention, measurement, review, automation, security practice, and process improvement. Its central principle is that quality is built into the development lifecycle rather than inspected into a finished product. Developers, testers, designers, security specialists, product owners, and operations personnel share responsibility for quality. This chapter examines how professional teams create unit, integration, API, end-to-end, accessibility, and security tests; how automation and static analysis support development; how debugging and code review reduce risk; and how quality metrics should be interpreted responsibly. Learning ObjectivesBy the end of this chapter, you should be able to: - Explain the purpose and appropriate scope of major web-testing levels.
- Design effective unit, integration, API, end-to-end, accessibility, and security tests.
- Apply test automation, mocks, stubs, fixtures, and test data management.
- Use code review, static analysis, and debugging systematically.
- Interpret quality metrics without confusing measurement with quality itself.
- Construct a practical quality strategy for a web application.
- Discuss ethical, privacy, governance, and accessibility responsibilities in testing.
1. Quality as an Engineering PropertyQuality is multidimensional. A web application may be functionally correct but unusable with a keyboard, fast but insecure, or secure but unavailable. Common quality attributes include: - Functional suitability: the system performs required tasks accurately and completely.
- Reliability: the system performs consistently under stated conditions.
- Performance efficiency: response time, throughput, and resource consumption are acceptable.
- Usability and accessibility: people can understand and operate the system, including users with disabilities.
- Security: confidentiality, integrity, authentication, authorization, and accountability are protected.
- Maintainability: the system can be changed, tested, and understood economically.
- Compatibility: the system works with relevant browsers, devices, protocols, and services.
Testing provides evidence about these qualities under selected conditions. It cannot prove that a non-trivial application contains no defects. Testing is sampling: a finite set of observations is used to evaluate behaviour across a much larger input and environment space. Consequently, test selection should be risk-based. Features involving money, personal data, authorization, safety, or legal obligations normally deserve stronger evidence than low-impact presentation details. Verification and validationVerification asks, “Are we building the product correctly?” It includes reviews, static analysis, type checking, and tests against technical specifications. Validation asks, “Are we building the correct product?” It includes acceptance testing, usability evaluation, stakeholder review, and observation of real user needs. A passing unit test cannot demonstrate that a checkout workflow is understandable or commercially appropriate. 2. The Testing Pyramid and Test ScopeA useful planning model is the testing pyramid: - Unit tests form the broad base. They are numerous, fast, and isolate small units.
- Integration and API tests occupy the middle. They verify collaboration among modules and services.
- End-to-end tests form a smaller upper layer. They exercise realistic user journeys through the deployed system.
The pyramid is a heuristic, not a law. A service-heavy application may require many contract and integration tests, while a safety-critical interface may justify substantial system-level testing. The important principle is to obtain fast, informative feedback at the lowest practical level and reserve expensive tests for behaviours that require broader context. A test should generally have four properties: - Deterministic: the same conditions produce the same result.
- Independent: failure in one test does not corrupt another.
- Focused: failure identifies a reasonably small cause.
- Repeatable: it works in local, continuous-integration, and release environments.
The common Arrange–Act–Assert pattern supports clarity: Arrange: construct inputs, fixtures, and dependencies
Act: perform one meaningful operation
Assert: compare observable results with explicit expectations 3. Unit TestingA unit is the smallest behaviour that can be tested meaningfully, such as a function, class method, validator, reducer, or domain service. A unit test normally replaces external dependencies with test doubles so that the unit’s own logic is examined. For example, a price-calculation function might accept item prices and a tax rate. Tests should include ordinary values, an empty collection, rounding boundaries, invalid rates, and large values. A good test describes a business rule rather than an implementation detail: test('applies tax to the subtotal', () => {
const total = calculateTotal([100, 50], 0.10);
expect(total).toBe(165);
});The example assumes monetary values are represented safely and that the function’s contract defines the rounding policy. In production, binary floating-point arithmetic may introduce errors for currency; integer minor units, such as cents, or a decimal library are usually safer. Test doubles- A stub supplies predetermined answers.
- A mock verifies interactions, such as whether a dependency was called with specified arguments.
- A fake is a lightweight working implementation, such as an in-memory repository.
- A spy records calls while retaining a real implementation or wrapping one.
Overuse of mocks can produce brittle tests that pass even when integrated behaviour is wrong. Prefer testing observable outcomes and use doubles where external systems, nondeterminism, cost, or isolation genuinely require them. Boundary-value and property-oriented thinkingMany defects occur at boundaries: zero versus one item, maximum field length, expiry at an exact timestamp, or a user lacking one permission. Equivalence partitioning divides inputs into groups expected to behave similarly; boundary-value analysis selects values at and around the edges of those groups. Property-based testing generates many inputs to check a general property. For example, a sorting function should preserve the input elements and produce an ordered result. Such a property is stronger than checking only a few fixed examples, but it requires carefully stated properties and controlled random seeds when reproducibility matters. 4. Integration and API TestingIntegration testing examines interactions among components. Examples include a controller with a database repository, an application with an identity provider, or a service consuming a message queue. Integration tests reveal configuration, serialization, transaction, network, and schema defects that unit tests intentionally omit. API testing treats an HTTP or event interface as a contract. A meaningful HTTP test checks more than status code: - method and route;
- authentication and authorization;
- request validation;
- status code;
- response schema and important fields;
- headers, caching, and content type;
- persistence or side effects;
- error behaviour and information disclosure;
- idempotency where relevant.
For example, a POST /orders test might verify that an authenticated customer can create an order, that an invalid product identifier produces a documented client error, that another customer cannot access it, and that a retry does not create an unintended duplicate when the API promises idempotency. Contract testingContract testing verifies an agreement between a service provider and its consumers. A consumer specifies the fields and behaviours it relies upon; the provider verifies that it satisfies those expectations. This can detect breaking changes earlier than a full end-to-end suite. Contracts should distinguish required from optional fields and should account for backward-compatible evolution. Integration environments and test dataA realistic integration environment may use containerized databases or ephemeral service instances. Test data should be isolated by test or test suite, generated from valid factories, and removed or reset reliably. Production personal data should not be copied into test systems unless a lawful, documented, privacy-preserving process exists. Masking or synthetic data is preferable. 5. End-to-End TestingEnd-to-end (E2E) testing exercises a complete user journey across the browser, application server, services, and data stores. Examples include registering, authenticating, adding an item to a basket, and completing a payment simulation. E2E tests provide high confidence about integration and deployment configuration but are slower and more fragile. Common causes of flakiness include arbitrary sleeps, unstable selectors, shared state, clock dependence, network variability, and third-party services. Prefer accessible roles or stable test identifiers over CSS classes that are likely to change. Wait for observable conditions, such as a response or visible state transition, rather than waiting a fixed number of milliseconds. A mature suite normally has a small set of critical smoke journeys, broader API and integration coverage, and extensive unit tests. E2E tests should not duplicate every data-validation case already covered at lower levels. 6. Accessibility TestingAccessibility testing evaluates whether people with disabilities can perceive, understand, navigate, and operate the application. It should be integrated from design through release rather than treated as a final audit. Important practices include: - use semantic HTML before adding custom behaviour;
- provide labels and instructions for form controls;
- maintain logical keyboard focus order;
- ensure visible focus indicators;
- provide text alternatives for meaningful images;
- communicate errors and dynamic updates to assistive technologies;
- maintain sufficient colour contrast and do not use colour as the only cue;
- support zoom and reflow without loss of content or function.
Automated tools can detect missing labels, invalid ARIA relationships, and some contrast or structural problems. They cannot reliably judge whether alternative text is meaningful, whether focus order is logical, or whether language is understandable. Therefore, automated scans must be supplemented by keyboard testing, screen-reader sampling, zoom/reflow checks, and testing with disabled users where feasible. Accessibility conformance should be related to an adopted standard, such as the Web Content Accessibility Guidelines (WCAG), and to applicable national law or institutional policy. Teams should document the target conformance level and exceptions rather than making unsupported claims of “fully accessible.” 7. Security TestingSecurity testing seeks evidence that threats are prevented, detected, or contained. It should be based on threat modelling and secure design, not only on scanning after implementation. Core areas include: - authentication, session management, and multifactor authentication;
- authorization at every protected operation, including object-level access;
- input validation and output encoding;
- protection against injection, cross-site scripting, cross-site request forgery, and request forgery to internal services;
- secure password storage and credential handling;
- transport and sensitive-data protection;
- dependency and supply-chain risk;
- logging, monitoring, rate limiting, and secure error handling.
A security test should state the identity, privileges, resource, and expected decision. For example, a test should verify that User A cannot retrieve User B’s invoice merely by changing an identifier in a URL. This is an authorization test, not simply an input-validation test. Security scanners are useful for finding known patterns and vulnerable dependencies, but they produce false positives and false negatives. Dynamic tests must be authorized, controlled, and conducted against non-production systems unless an explicit operational procedure permits otherwise. Never test destructive payloads against systems without written permission and rollback planning. 8. Automation and Continuous QualityAutomation provides speed, repeatability, and continuous feedback. A typical pipeline may perform the following sequence: - format and lint the code;
- compile or type-check it;
- run unit tests;
- run component, integration, and API tests;
- perform dependency and static security checks;
- build an artifact;
- deploy to an ephemeral or staging environment;
- run smoke and selected E2E tests;
- publish results and quality evidence.
A failed quality gate should be actionable. Teams should distinguish a product defect from an infrastructure failure, test defect, timeout, and environmental issue. Flaky tests must be quarantined only temporarily and assigned for repair; permanently ignoring them destroys trust in the suite. Test automation code is production code. It needs naming conventions, review, refactoring, dependency management, and secure handling of credentials. Secrets should come from a managed secret store or protected pipeline variables, never from committed test files. 9. Code Review and Static AnalysisCode review is a human examination of a change before integration. Reviewers should assess correctness, security, accessibility, performance, maintainability, observability, test adequacy, and compatibility with existing contracts. A concise change with a clear description is easier to review than a large mixed-purpose commit. A useful review process is: - author explains purpose, risk, and test evidence;
- automated checks run before review;
- reviewers inspect behaviour and important edge cases;
- comments distinguish defects, questions, and optional improvements;
- author responds and updates the change;
- the final decision and unresolved risk are recorded.
Static analysis examines source or compiled representations without executing the application. It includes linters, type checkers, complexity analysis, secret detection, dependency scanners, and security rules. Static tools can identify unreachable code, unsafe APIs, inconsistent style, and some injection risks. They cannot establish that business requirements are correct. Rules should be configured to reflect the project, and warnings should be triaged rather than blindly suppressed. 10. Debugging as Systematic InvestigationDebugging is the process of locating, explaining, and correcting the cause of an observed failure. A disciplined method is more effective than repeatedly changing code: - reproduce the failure with the smallest reliable case;
- record inputs, environment, logs, timestamps, and recent changes;
- classify the symptom and form competing hypotheses;
- use assertions, traces, a debugger, or targeted experiments;
- identify the root cause rather than only the visible symptom;
- implement the smallest sound correction;
- add or improve a regression test;
- verify adjacent behaviour and document the lesson.
Logs should contain correlation identifiers and useful contextual fields while excluding passwords, tokens, and unnecessary personal data. In distributed systems, tracing and structured logs help connect a browser action to server requests and downstream calls. Debugging production incidents requires care: preserve evidence, avoid exposing confidential information, and use controlled mitigations such as feature flags or rollback when appropriate. 11. Quality Metrics and Their InterpretationMetrics support decisions when they are tied to a question. Examples include: - Defect density: defects divided by a defined size measure, such as thousand lines of code or function points. Because size measures and defect definitions vary, comparisons require consistent methods.
- Defect escape rate: the proportion of defects discovered after a chosen quality boundary. If 8 defects are found after release and 92 were found before release, the post-release proportion is 8 ÷ (8 + 92) = 0.08, or 8%, assuming all defects are counted under the same classification rules.
- Test pass rate: passed tests divided by executed tests. It is misleading if important tests are absent, skipped, or flaky.
- Code coverage: the proportion of executed statements, branches, or other measured elements. Coverage indicates what ran, not whether the behaviour was correctly asserted.
- Mean time to restore: average time from service-impacting failure to restoration. It is useful operationally but should be interpreted with incident severity and detection time.
Metrics are indicators, not objectives to maximize blindly. A team can increase coverage by writing trivial assertions, reduce defect counts by redefining defects, or improve pass rates by excluding difficult tests. Balanced dashboards combine quantitative measures with review findings, escaped defects, user feedback, accessibility results, security risk, and service-level outcomes. 12. An Illustrative Case: Campus Events PlatformBackgroundA university develops an events platform where students browse events, reserve limited places, and receive confirmation. The system has a React front end, an HTTP API, a relational database, and an email provider. ProblemDuring a pilot, two users occasionally reserved the final available place simultaneously. A keyboard user could not reach the date picker, and a student discovered that changing an event identifier in the URL exposed another user’s reservation. AnalysisThe first defect was a concurrency and transaction problem. A unit test of the reservation calculation could pass while two API requests still observed the same remaining capacity. The accessibility problem was a component semantics and focus-management defect. The privacy problem was an object-level authorization failure. Approach and solutionThe team introduced: - unit tests for capacity rules, including zero and full capacity;
- an integration test using concurrent requests against a real database;
- an API test verifying that unauthorized reservation identifiers return the documented denial response;
- E2E tests for browsing, reserving, cancellation, and confirmation;
- keyboard and screen-reader testing of the date picker;
- a database transaction with an appropriate integrity constraint or locking strategy;
- code review requirements for authorization checks on every resource endpoint;
- dependency, static-analysis, and secret-detection checks in continuous integration.
The API contract also documented whether a repeated reservation request is safe to retry. Test data was generated rather than copied from live students. OutcomeThe concurrency defect was prevented at the data boundary rather than only at the user-interface layer. The authorization test became a permanent regression test. The date picker was rebuilt using semantic controls and explicit focus management. The team reported the known limitations of automated accessibility scanning instead of treating a clean scan as proof of accessibility. Learning pointsA quality strategy must test the layer where a failure can occur. Business logic belongs in unit tests, data consistency requires integration evidence, permissions require negative tests, and accessibility requires human interaction as well as automation. No single metric or test level was sufficient. 13. Challenges, Ethics, and GovernanceTesting consumes time and infrastructure, and complete coverage is impossible. Legacy systems may be difficult to isolate; third-party services may be unavailable; nondeterministic timing may create flaky tests; and teams may lack accessibility or security expertise. These limitations should be made visible through risk registers, documented assumptions, and prioritized remediation. Ethically, testers must minimize harm. They should obtain authorization, protect personal data, avoid unnecessary surveillance, report vulnerabilities responsibly, and ensure that testing does not exclude disabled users. Security findings should be communicated to appropriate owners with enough detail to remediate but not casually disclosed to enable misuse. Automated tools, including AI-assisted coding and test generation, require human review: generated tests may encode incorrect assumptions, expose confidential code, or create a false impression of coverage. Governance should define release criteria, ownership of defects, evidence retention, access controls, vulnerability response, and exceptions. Regulated or high-impact systems may require traceability from requirements to tests and approvals. Quality gates should be proportionate to risk and should support learning rather than encourage concealment. 14. Contemporary Professional PracticeCurrent web engineering increasingly uses ephemeral environments, containerized test services, contract testing, observability-driven testing, and production-safe synthetic monitoring. Feature flags allow controlled rollout, while canary releases compare behaviour before broad deployment. Mutation testing deliberately introduces small code changes to evaluate whether tests detect them; it can reveal weak assertions but may be computationally expensive. AI tools can assist with test-case suggestions, failure clustering, code explanation, and generation of boundary inputs. They do not replace requirements analysis, threat modelling, accessibility expertise, or human accountability. Teams should verify generated code, avoid sending sensitive material to unapproved services, and record important decisions made with automated assistance. Best-Practice Checklist- Begin with risks, requirements, and user journeys.
- Keep unit tests fast and focused.
- Test real boundaries through integration and API tests.
- Maintain a small, reliable set of high-value E2E journeys.
- Include negative, authorization, failure, and recovery cases.
- Test accessibility with automation and human interaction.
- Use authorized, privacy-preserving security testing.
- Treat test and production code with comparable engineering discipline.
- Review changes for correctness, security, accessibility, and maintainability.
- Investigate flaky tests rather than normalizing them.
- Interpret coverage and pass rates alongside escaped defects and user outcomes.
- Record assumptions, exceptions, residual risks, and ownership.
Chapter SummarySoftware quality engineering integrates prevention, verification, validation, testing, review, automation, security, accessibility, debugging, and measurement. Unit tests efficiently examine isolated logic; integration and API tests validate collaboration and contracts; E2E tests provide realistic but expensive system evidence. Accessibility requires semantic design, automated checks, keyboard and assistive-technology evaluation, and user-centred consideration. Security testing must include authentication, authorization, input handling, dependency risk, and responsible operational practice. Automation and continuous integration provide rapid feedback, while code review and static analysis detect issues before deployment. Debugging is a structured investigation that should end with a regression test. Metrics such as coverage, escape rate, and restoration time are useful only when definitions and limitations are clear. A professional quality strategy is risk-based, evidence-driven, ethical, and shared across the development team. Key TermsSoftware quality engineering: The disciplined integration of quality practices throughout software development. Verification: Evaluating whether implementation conforms to specified requirements. Validation: Evaluating whether the system satisfies actual stakeholder and user needs. Unit test: A test of a small, isolated software unit. Integration test: A test of interactions among components or services. API test: A test of an application programming interface and its contract. End-to-end test: A test of a complete workflow across the system. Test double: A substitute used to control or observe a dependency in testing. Stub: A test double that supplies predetermined responses. Mock: A test double used principally to verify interactions. Contract testing: Verification that a service satisfies agreed consumer expectations. Regression test: A test that guards against reintroduction of a corrected defect. Flaky test: A test whose result changes unpredictably without a relevant product change. Accessibility testing: Evaluation of usability by people with varied abilities and assistive technologies. Static analysis: Examination of code without executing it. Code review: Human inspection of a proposed software change. Threat modelling: Systematic identification of assets, threats, vulnerabilities, and mitigations. Code coverage: A measure of which selected code elements were executed by tests. Defect escape rate: The proportion of classified defects discovered beyond a chosen quality boundary. Review QuestionsShort Answer Questions- Distinguish verification from validation.
- What is the purpose of a test double?
- Why are E2E tests usually fewer than unit tests?
- Why can automated accessibility scanning not establish complete accessibility?
- What does code coverage measure, and what does it not prove?
Long Answer Questions- Explain the testing pyramid and discuss circumstances in which a project may need to adapt it.
- Describe a professional API-testing strategy for an authenticated resource.
- Explain how code review, static analysis, and automated tests complement one another.
- Discuss the ethical and governance requirements of security testing.
- Develop a quality strategy for a web application that stores personal information.
Analytical / Application Questions- An application has high unit-test coverage but frequent production failures caused by database transactions and authorization. Analyze why the existing strategy is inadequate and propose additional tests.
- A team’s test pass rate is 99%, but many tests are skipped and users report accessibility problems. Evaluate the metric and recommend a balanced evidence set.
- Design a test plan for a limited-capacity event reservation API that must prevent overbooking, protect user privacy, support keyboard users, and tolerate request retries.
Multiple Choice Questions with AnswersQuestion 1Which testing level is most appropriate for verifying an isolated tax-calculation function? A. Unit testing B. End-to-end testing C. Load testing D. Exploratory production testing Correct Answer: A. Unit testing Question 2What is the primary purpose of a contract test? A. To measure browser rendering speed B. To verify an agreed interface between a service and its consumer C. To replace all security testing D. To test a user’s keyboard technique Correct Answer: B. To verify an agreed interface between a service and its consumer Question 3Which practice most directly reduces flaky browser tests? A. Adding arbitrary long sleeps B. Sharing mutable state across tests C. Waiting for observable conditions D. Selecting elements by changing CSS classes Correct Answer: C. Waiting for observable conditions Question 4Which statement about code coverage is correct? A. It proves that all requirements are correct B. It measures selected code elements executed by tests C. It guarantees accessibility compliance D. It replaces code review Correct Answer: B. It measures selected code elements executed by tests Question 5A test verifies that User A cannot retrieve User B’s invoice by changing an identifier. What security property is primarily being tested? A. Compression B. Object-level authorization C. Database indexing D. Visual contrast Correct Answer: B. Object-level authorization Question 6Which is the strongest accessibility-testing approach? A. Rely only on an automated scanner B. Check only colour contrast C. Combine automated checks with keyboard, assistive-technology, and user-focused evaluation D. Test only on a developer’s preferred browser Correct Answer: C. Combine automated checks with keyboard, assistive-technology, and user-focused evaluation Question 7What is the main risk of excessive mocking in unit tests? A. Tests may become coupled to implementation and miss integration defects B. The application will always become slower C. It prevents all test isolation D. It automatically exposes production secrets Correct Answer: A. Tests may become coupled to implementation and miss integration defects Question 8If 8 defects are found after release and 92 before release, what is the post-release proportion under the chapter’s stated definition? A. 8% B. 10% C. 80% D. 92% Correct Answer: A. 8% Question 9Which activity is an example of static analysis? A. Executing a checkout journey in a browser B. Inspecting source code for unsafe API usage without running it C. Interviewing a user about usability D. Sending a real payment to a provider Correct Answer: B. Inspecting source code for unsafe API usage without running it Question 10What should normally happen after a production defect is corrected? A. Delete related tests to avoid future failures B. Add or improve a regression test C. Suppress all static-analysis warnings D. Copy production personal data into the test suite Correct Answer: B. Add or improve a regression test
|