7.2.4 Test strategy
In commercial software development the programmer is typically required to ensure their code is as error free as possible before it is handed over to the testing team whose job is to identify errors in the presented program. With large and complex programs, it is vital that all aspects of the code be tested systematically and comprehensively. In order for the testing team to ensure comprehensiveness, they make use of a document called a test strategy. We will be producing a test strategy (in part) for Assignment B.
A test strategy is document that specifies how your class or program will be tested and, after testing, provides documentation of all the tests done, including the results of the tests. This document should be developed before the program code is written as it is important to think about testing before you have an emotional investment in the outcome. Many designers draft up the test strategy based on the class diagram they present to the programmer to implement. The comprehensiveness of the class diagram is therefore important as it provides an overall high-level view of the system.
There are many approaches to developing a test strategy and different organisations will adopt their own specific strategy. In this unit we will learn how to develop a very basic strategy, which typically contains two components:
A test plan. This is a summary of all the tests which will be eventually performed on the program.
The actual tests. These are the specifications/details of all the tests listed in the test plan. Each test will contain the following three components:
Test data
Expected results
Actual results.
When drafting a test strategy before the program has been developed, the only parts of the document that are left incomplete are the actual results. These are added after the program has been implemented and the tests have been run. When this is done, the actual results will be compared to the expected results and if they match then the test was successful. If they don't match then this indicates a problem in the program, or sometimes it indicates there is a problem with the test data.
Testing strategy example
Let's consider the following class shown in the diagram below:
We have been provided with the following specification details:
StaffId is a string of three numeric characters.
Pay Scale is a single character from the set {A, B, C, X}.
Employee Status is either true to indicate currently employed, or false to indicate not currently employed.
Test plan
The following steps outline an approach to a test plan:
Create an Employee object with the default constructor.
Create an Employee object with the non-default constructor:
with valid field values
with invalid field values
Test all get methods:
Test getIsEmployee()
Test getPayScale()
Test getStaffId()
Test all set methods:
Test setIsEmployee()
with valid field values
with invalid field values
Test setPayScale()
with valid field values
with invalid field values
Test setStaffId()
with valid field values
with invalid field values
It is important to note the following:
In the test plan, every method which has a formal parameter must involve both positive and negative testing.
If a class has multiple parameterised constructors, then each constructor should be specified as an independent test case.
A negative test could involve multiple values. When this happens, each value being tested is shown as an independent subcase under the same test heading.
The actual tests
Test 1
Create an Employee object with the default constructor.
Test data:
staffId: "000"
payScale: 'X'
isEmployee: false
Expected results:
staffId: "000"
payScale: 'X'
isEmployee: false
Actual results:
<added after the program has been developed>
Test 2.1
Create an Employee object with the non-default constructor with valid field values.
Test data:
staffId: "517"
payScale: 'X'
isEmployee: false
Expected results:
staffId: "517"
payScale: 'X'
isEmployee: false
Actual results:
<added after the program has been developed>
Test 2.1b.1
Create an Employee object with the non-default constructor with invalid field values.
Test data:
staffId: "99"
payScale: 'a'
isEmployee: true
Expected results:
staffId: "517"
payScale: 'X'
isEmployee: false
Actual results:
<added after the program has been developed>
Test 2.1b.2
Create an Employee object with the non-default constructor with invalid field values.
Test data:
staffId: "abc"
payScale: '6'
isEmployee: true
Expected results:
staffId: "000"
payScale: 'X'
isEmployee: false
Actual results:
<added after the program has been developed>
The rest of the methods can be elaborated similarly to document the rest of the test strategy.
After the test strategy document has been completed, the implementation of the program can begin (the testing of the implementation would use a Test class—we will not be using that here as our platform doesn't support embedding multiple classes, so we will show a similar output with a test method).
Once the program has been implemented, the last part of the test strategy document is completed by appending the result of the execution for the specific method and affixing any screenshot available of the execution along with any applicable comments as follows:
Test 1
Create an Employee object with the default constructor.
Test data:
staffId: "000"
payScale: 'X'
isEmployee: false
Expected results:
staffId: "000"
payScale: 'X'
isEmployee: false
Actual results:
Test Passed!
Test 2.1
Create an Employee object with the non-default constructor with valid field values.
Test data:
staffId: "517"
payScale: 'C'
isEmployee: false
Expected results:
staffId: "517"
payScale: 'C'
isEmployee: false
Actual results:
Test Passed!
Test 2.1b.1
Create an Employee object with the non-default constructor with invalid field values.
Test data:
staffId: "99"
payScale: 'a'
isEmployee: true
Expected results:
staffId: "517"
payScale: 'X'
isEmployee: false
Actual results:
Test FAILED!
Method not validating length of staffId and value of payScale correctly.
Test 2.1b.2
Create an Employee object with the non-default constructor with invalid field values.
Test data:
staffId: "abc"
payScale: '6'
isEmployee: true
Expected results:
staffId: "517"
payScale: 'X'
isEmployee: false
Actual results:
Test Failed!
Method not validating value of staffId and payScale correctly.
The remaining methods should be tested along the same lines.
As we can see from the above demonstration, the test strategy is a living and typically very long document. While it is relatively simple to perform tests, it is time consuming to invoke each and every method one at a time, capture and then document the final result. While the process is painstaking, it is critical in software development.
The code provided above shows the tests commented out because only one test is generally executed at one time so as not to influence the value based on another method. Depending on the object and the test, this can be modified.