everything.study
COMP 4004

Software Quality Assurance

0.5 credit · Undergraduate

Introduction to the theory and practice of Software Quality Assurance. Topics include: equivalence partitioning, test-driven testing, unit testing patterns, refactoring, software metrics, requirements engineering, scenario modeling and acceptance testing, model-based testing, state machine testing, software testing theory and tools.

Study notes

Independent, AI-drafted study guide · Public sources linked below · Added November 5, 2026

Original guide aligned to the public Fall 2026 outline. The outline identifies course topics; explanations and exercises are independently written.

Requirements, tests, and oracles

A test compares observed behavior with an expected result called an oracle. A specification can be incomplete, so passing tests may only show consistency with the checks that were written. Verification asks whether an artifact satisfies its stated specification; validation asks whether the resulting behavior addresses the intended need. Distinguish unit, integration, system, and acceptance tests by the boundary and purpose being examined. A test suite should make an actual failure meaningful, reproducible, and diagnosable.

References: Valente — Software Engineering: A Modern Approach, Testing

Equivalence partitions and boundaries

Equivalence partitioning divides inputs into classes expected to exercise similar behavior. Select representatives from valid and invalid classes, then test boundaries where implementation errors often occur. For a permitted age interval 18 through 65 inclusive, useful values include 17, 18, 19, 64, 65, and 66. This is an original toy rule, not a course assignment. Missing, nonnumeric, and fractional input are separate questions if the interface accepts them. Document assumptions rather than silently assigning unspecified behavior.

References: Valente — Software Engineering: A Modern Approach, Testing

Test-driven development and design feedback

TDD alternates a small failing test, a minimal implementation that passes it, and refactoring while the tests stay green. The initial failure confirms that the new test can detect the missing behavior. Small steps expose dependencies and encourage interfaces that can be exercised independently. TDD does not remove the need for broader acceptance, usability, security, or performance evidence. A perfectly tested component can still be integrated incorrectly or implement the wrong requirement.

References: Valente — Software Engineering: A Modern Approach, Testing

Structural coverage and mutation

Statement coverage asks whether statements ran; branch coverage asks whether decision outcomes were exercised. Neither establishes that assertions would detect a wrong result. Consider a function with both branches executed but no assertion on its return value: high coverage can coexist with an ineffective test. Mutation testing introduces small changes and checks whether tests detect them. Surviving mutations can reveal missing observations, though some mutations are equivalent to the original program and require interpretation.

References: Valente — Software Engineering: A Modern Approach, Testing

Model-based and state-machine testing

A state model specifies allowed transitions and the conditions under which they occur. Generate tests that reach states, exercise transitions, and check invariants before and after events. For a door model, lock may be allowed only from Closed; testing lock from Open checks rejection behavior. Sequence matters because the same input can be valid in one state and invalid in another. Keep the model independently understandable so that tests do not merely reproduce the implementation's own mistake.

References: Valente — Software Engineering: A Modern Approach, Testing

Isolation, refactoring, and quality metrics

A deterministic unit test controls time, randomness, network replies, and other relevant external effects. A fake dependency provides simple behavior; a mock additionally checks expected interactions, which can make tests tightly coupled to implementation. Refactoring changes structure while preserving behavior, so meaningful regression tests are a useful safety net. Metrics such as complexity, defect density, and coverage are indicators with denominators and context. Optimizing a metric alone can encourage superficial changes that fail to improve user-visible quality.

References: JUnit official user guide — test structure and execution · Valente — Software Engineering: A Modern Approach, Refactoring

Practice & worked answers

Original exercises for this guide.

1. A suite has 100% statement coverage but never checks returned values. What can be concluded?

Show worked answer

The executed tests reached all instrumented statements, but there is little evidence that wrong answers would be detected. Add assertions derived from the behavior contract and consider branches, boundaries, and fault sensitivity.

2. Why test the sequence open, lock, close rather than testing each command only from the initial state?

Show worked answer

The sequence explores state-dependent validity. Locking an open door should be handled according to the model, and the rejection must not corrupt later state. Independent single-step tests can miss transition and recovery defects.

Public notes & references

Course sources establish the scope above. Reference materials support the explanations; linked lecture notes and books remain with their authors.

Topics

Course catalogue

Prerequisites & course information

Precludes additional credit for SYSC 4101 . Prerequisite(s): COMP 3004 . Lectures three hours a week.

Sources