NivaarExam PrepOfficial exam papers ↗

25-Comp-A6 Software Engineering · May 2015

Question 4 of 8: Software Testing

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

National Exams — May 2015 — 98-Comp-A6 Software Engineering. Three-hour, closed-book exam, no calculator permitted. Format: eight questions, candidates answer any five (all questions equal weight — each of the five counted questions is worth 20%; only the first five questions as they appear in the answer book are marked). All eight questions are solved below for completeness.

Reference texts: Sommerville, Software Engineering (10th ed., Pearson) — software process models, function-oriented and object-oriented design, software testing, distributed software engineering, real-time software engineering, reliability engineering, verification and validation; Pressman, Software Engineering: A Practitioner's Approach (9th ed.) — supplementary process/testing coverage; Coulouris, Dollimore, Kindberg & Blair, Distributed Systems: Concepts and Design — scalability, distributed objects, client-server architectures.

Question 4: Software Testing (a) 5, (b) 5, (c) 10 — 20 marks

Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.

(a) Functional vs. Structural Testing

Functional (black-box) testing derives test cases purely from the specification, without reference to the program's internal structure: the tester picks inputs (typically using equivalence partitioning and boundary-value analysis) that exercise the documented behaviour and checks the outputs against expected results. Structural (white-box) testing derives test cases from the program's internal structure — its control-flow graph — aiming to exercise specific code elements (statements, branches, or paths) that a black-box test suite might never reach, since it is derived without knowledge of which inputs actually traverse which code paths.

The two are complementary rather than substitutes: functional testing confirms the system does what the specification says it should, but says nothing about code paths the specification never anticipated (an unhandled error branch, say); structural testing can guarantee a coverage criterion (e.g. every branch executed at least once) but a path being executed and producing the correct result are different things — structural testing alone cannot tell you what the correct result should be. The standard defect-testing process therefore uses functional testing first to derive a specification-based test suite, then measures the structural coverage that suite achieves and adds further, structurally-motivated test cases to exercise any statements or branches the functional suite missed (a common target is 100% branch coverage for safety- or business-critical code).

(b) Defect-Freedom, Testing and Fitness for Purpose

It is not necessary (and for any non-trivial system, not practically achievable) for delivered software to be completely free of defects, because the cost of finding and removing defects rises steeply as the remaining defect density falls, while the marginal value of removing one more low-impact defect approaches zero — economically rational testing stops once the expected cost of the remaining defects (weighted by their probability of occurring and their severity) is lower than the cost of continuing to test. What matters to the customer is not zero defects but that the system is fit for purpose: reliable enough, and free enough of high-consequence defects, that it delivers acceptable value in its intended use.

Testing can validate fitness for purpose only to a bounded extent. As Dijkstra observed, testing can show the presence of defects but never their absence — a passing test suite proves only that the specific inputs tried produced correct results, not that every possible input will. Testing therefore validates fitness for purpose in proportion to how representative the test suite is of real operational use (which is why techniques such as operational-profile testing, weighting test cases by how often each input class occurs in the field, give a more meaningful reliability estimate than an arbitrary functional test suite) and must be supplemented, for higher-assurance systems, with complementary V&V activities such as inspection, static analysis or formal verification that reason about the program directly rather than sampling its behaviour.

(c) Testing Strategy for the Home Security System

The HSS's four object categories (sensors, coordination/configuration, keypad, actuators) drive a four-level strategy, escalating from isolated unit tests to system-level scenario tests:

LevelTargetTechniqueExample
UnitEach Sensor subclass, Configuration, Keypad, each actuatorStructural (branch coverage) with stubbed I/OTemperatureSensor.poll() returns the correct event for readings above/at/below its threshold
IntegrationSensor → SecuritySystem → actuator chainsFunctional, equivalence-partitioned by fault classa FloodSensor event correctly triggers AlarmController but not LightController
SystemWhole HSS against realistic scenariosScenario/use-case based, black-boxintrusion during Armed state with entry delay running, then disarmed correctly before the delay expires; the same sequence with an incorrect PIN
Non-functionalTiming, concurrency, robustnessStress/timing tests, structural on concurrent-access pathsall four sensor types triggering simultaneously; keypad programming attempted while an alarm is active

Because a life-safety system is involved (smoke and flood detection), the strategy weights structural coverage more heavily than a typical business system would — every branch of SecuritySystem.handleEvent() should be exercised, including failure and timeout paths (PhoneDialer unable to reach any number, sensor readings outside expected range) that a purely scenario-driven functional suite is likely to under-test. Regression testing after any Configuration change (a new threshold or delay) is also required, since the whole point of owner-programmability is that behaviour changes at runtime without a new release.