Software Testing: Techniques and Strategies
Software Testing Goals
The software testing process has two primary goals:
- Validation: Demonstrate to developers and customers that the software fulfills the specified requirements.
- Defect Detection: Uncover faults or defects within the software that lead to incorrect, undesirable, or specification-violating behavior.
The first goal drives validation testing, where the system’s correctness is evaluated against a set of test cases reflecting expected usage patterns. The second goal leads to defect testing, employing test cases designed to expose flaws.
It’s crucial to understand that testing cannot guarantee the complete absence of defects or ensure adherence to specifications under all circumstances. There’s always a possibility of undiscovered issues.
Experience-Based Testing
Testing strategies can leverage experience gained from system usage, focusing on operational features. For instance:
- Test the accessibility of all system functions through menus.
- Test combinations of functions accessed through the same menus (e.g., text formatting options).
- Test all functions with both correct and incorrect user inputs.
System Testing
System testing involves integrating two or more components implementing system functions or features and testing the resulting integrated system. Complex systems often involve two distinct phases:
Integration Testing
In this phase, the test team requires access to the system’s source code. Upon discovering a problem, they aim to pinpoint the source and identify components needing debugging.
Purpose: System integration involves constructing a system from its components and testing the resulting system for issues arising from component interactions.
Release Testing
Here, a potentially releasable version of the system undergoes testing. The team focuses on validating requirement compliance and ensuring system reliability.
Purpose: Release testing focuses on a system version intended for customer distribution. The primary goal is to boost supplier confidence in the system’s ability to meet requirements.
Performance Testing
Once the system is fully integrated, it can be evaluated for emergent properties like performance and reliability.
Component Testing
Also known as unit testing, component testing focuses on individual system components. As a defect-finding process, its goal is to expose defects within these components. Various component types can be tested:
- Individual functions or object methods.
- Object classes with multiple attributes and methods.
- Composite components combining different objects or functions, accessed through defined interfaces.
Interface Testing
Interface testing is particularly critical in object-oriented and component-based development. Objects and components are defined by their interfaces, enabling reuse in conjunction with other components across different systems. Interface types include:
- Parameter Interfaces: Data or function references are passed between components.
- Shared Memory Interfaces: Components share a memory segment.
- Procedural Interfaces: One component exposes a set of procedures callable by other components.
- Message Passing Interfaces: Components request services from each other by exchanging messages.
Common Interface Errors
Interface errors are prevalent in complex systems. Some common types include:
- Interface Misuse: A component calls another incorrectly.
- Interface Misunderstanding: A calling component makes assumptions about the called component’s behavior due to specification misinterpretation.
- Timing Errors: These occur in real-time systems using shared memory or message passing interfaces.
Interface Testing Guidelines
- Review the code and explicitly list each call to external components.
- Always test interfaces with null pointers.
- Design tests to trigger failures in components called via procedural interfaces.
- Employ stress testing in message passing systems.
- For components interacting through shared memory, design tests varying their activation order.
Test Case Design
Designing test cases is part of component and system testing. It involves devising inputs and expected outputs to exercise the system. Several approaches exist:
- Requirements-Based Testing: Test cases are designed to verify system requirements.
- Partition Testing: Input and output partitions are identified, and tests are designed to cover all partitions.
- Structural Testing: Leverages program structure knowledge to design tests exercising all parts of the code.
Path Testing
A structural testing strategy aiming to execute every possible execution path within a component or program. Running each path independently ensures that all statements are executed at least once.
Test Automation
Testing is a resource-intensive phase in software development. Consequently, testing tools are often among the first to be developed. A software testing workbench comprises an integrated set of tools supporting the testing process. Examples include:
- Test Manager: Manages test program execution, tracking test data, expected results, and resources.
- Test Data Generator: Generates test data for the program under test.
- Oracle: Predicts expected test results.
- File Comparator: Compares test program outputs with previous results, reporting differences.
- Report Generator: Provides resources for documenting and reporting test results.
- Dynamic Analyzer: Instruments the program to track statement execution counts, generating an execution profile.
- Simulator: Simulates the target execution environment.
Key Points in Software Testing
- Testing can only reveal the presence of errors, not their absence.
- Component testing is typically the developer’s responsibility, while a separate team usually handles system testing.
- Integration testing, the initial system testing activity, focuses on defect detection in integrated components. Release testing emphasizes validating client-ready releases against requirements.
- System testing should aim to”brea” the system, leveraging experience and guidelines for selecting effective test cases.
- Interface testing aims to uncover defects in component interfaces, which can arise from specification misinterpretations, timing assumptions, or other errors.
- Equivalence partitioning helps derive test cases by identifying input and output partitions and exercising the program with values from these partitions. Boundary values often prove most effective.
- Structural testing analyzes program structure to guide test case selection.
- Automation reduces testing costs by providing tool support.