CS504Testing
CS504Testing
Software Engineering - I
An Introduction to Software Construction Techniques for Industrial
Strength Software
Software Testing
To understand the concept of software testing correctly, we need to understand a few
related concepts.
Defect
The second major and a very important concept is Defect. A defect is a variance from a
desired product attribute. These attributes may involve system specifications well as user
expectation. Anything that may cause customer dissatisfaction, is a defect. Whether these
defects are in system specifications or in the software products, it is essential to point
these out and fix.
Therefore software defect is that phenomenon in which software deviates from its
expected behavior. This is non-compliance from the expected behavior with respect to
written specifications or the stakeholder needs.
Software Testing
With these concepts, we are in a position to define software testing. Software testing is
the process of examining the software product against its requirements. Thus it is a
process that involves verification of product with respect to its written requirements and
conformance of requirements with user needs. From another perspective, software testing
is the process of executing software product on test data and examining its output vis-à-
vis the documented behavior.
Successful Test
From the following sayings, a successful test can be defined
“If you think your task is to find problems then you will look harder for them than if you
think your task is to verify that the program has none” – Myers 1979.
The success of a test depends upon the ability to discover a bug not in the ability to prove
that the software does not have one. As, it is impossible to check all the different
scenarios of a software application, however, we can apply techniques that can discover
potential bugs from the application. Thus a test that helps in discovering a bug is a
successful test. In software testing phase, our emphasis is on discovering all the major
bugs that can be identified by running certain test scenarios. However it is important to
keep in mind that testing activity has certain limitations.
Limitations of testing
With the help of the following example, we shall see how difficult it may become to
discover a defect from a software application.
a b Expected
result
“cat” “dog” False
“” “” True
“hen” “hen” True
“hen” “heN” False
“” “” False
“” “ball” False
“cat” “” False
“HEN” “hen” False
“rat” “door” False
“ ” “ ” True
Results of testing
The tester runs all the above-mentioned test cases and function returns the same results as
expected. But it is still not correct. What can be a problem To analyze the problem, lets
look at the code of this string equal routine
Testing limitations
• In order to prove that a formula or hypothesis is incorrect all you have to do to
show only one example in which you prove that the formula or theorem is not
working.
• On the other hand, million of examples can be developed to support the
hypothesis but this will not prove that it is correct.
• These examples only help you in coming up with a hypothesis but they are not
proves by themselves and they only enhance your comfort level in that particular
hypothesis or in this particular case, in your piece of software.
• You cannot test a program completely because:
o the domain of the possible inputs is too large to test.
o there are too many possible paths through the program to test.
• According to Discrete Mathematics
o To prove that a formula or hypothesis is incorrect you have to show only
one example.
o To prove that it is correct any numbers of examples are insufficient. You
have to give a formal proof of its correctness.
At the time when these two activities are being performed, merely initial design of the
application is completed. Therefore, tester uses his/her imagination to come up with use
patterns of the application that can help him/her in describing exact steps that should be
executed in order to test a particular functionality. Moreover, tester needs to figure out
loose points in the system from where he/she can discover defects. All these activities are
highly imaginative and a tester is supposed to possess above average (if not excellent)
analytical skills.
We shall explain the testing activities parallel to development activities with the help of
the following diagram
FS
Analysis,
Test Planning, Analysis,
Test Case and Design,
Activities Test Data
Preparation
Coding Activities
carried out carried out
by the testing by the
team Test Case development
Execution and
Defect Bug Fixing team
Reporting
Description
• Functional specification document is the starting point, base document for both
testing and the development
• Right side boxes describe the development, whereas, left side boxes explain the
testing process
• Development team is involved into the analysis, design and coding activities.
• Whereas, testing team too is busy in analysis of requirements, for test planning,
test cases and test data generation.
• System comes into testing after development is completed.
• Test cases are executed with test data and actual results (application behavior) are
compared with the expected results,
• Upon discovering defects, tester generates the bug report and sends it to the
development team for fixing.
• Development team runs the scenario as described in the bug report and try to
reproduce the defect.
• If the defect is reproduced in the development environment, the development
team identifies the root cause, fixes it and sends the patch to the testing team
along with a bug resolution report.
• Testing team incorporates the fix (checking in), runs the same test case/scenario
again and verifies the fix.
• If problem does not appear again testing team closes down the defect, otherwise,
it is reported again.
Usefulness of testing
Objective of testing is to discover and fix as many errors as possible before the software
is put to use. That is before it is shipped to the client and the client runs it for acceptance.
In software development organizations, a rift exists between the development and the
testing teams. Often developers are found questioning about the significance or even need
to have the testing resources in the project teams. Whoever doubts on the usefulness of
the testing team should understand what could happen if the application is delivered to
client without testing? At the best, the client may ask to fix all the defects (free of cost)
he would discover during the acceptance testing. At the worst, probably he would sue the
development firm for damages. However, in practice, clients are often seen complaining
about the deliverables and a couple of defected deliverables are sufficient for breaking
the relations next to the cancellation of contract.
Therefore, it should be well preserved among the community of developers that testers
are essential rather inevitable. A good tester has a knack of smelling errors – just like
auditors and it is for the good of the organization not to harm it.
Effective testing
The objective of testing is to discover the maximum number of defects with a minimum
number of resources before the system is delivered to the next stage. Now the question
arises here how to increase the probability of finding a defect?
As, good testing involves much more than just running the program a few times to see
whether it works or not. A good tester carries out a thorough analysis of the program to
devise test cases that can be used to test the system systematically and effectively.
Problem here is how to develop a representative set of test cases that could test a
complete program. That is, selection of a few test cases from a huge set of possibilities.
What should be the sets of inputs that should be used to test the system effectively and
efficiently?
String matching
Organization
For equivalence partitions, we divide the problem in two obvious categories of equal
strings and the other one for unequal strings. Within these equivalent partitions, further
partitioning is done. Following is the description of the equivalence partitions and their
test cases
Equal
• Two equal strings of arbitrary length
o All lower case “cat” “cat”
o All upper case “CAT” “CAT”
o Mixed case “Cat” “Cat”
o Numeric values “123” “123”
o Two strings with blanks only “ ” “ ”
o Numeric and character mixed “Cat1” “Cat1”
o Strings with special characters “Cat#1” “Cat#1”
• Two NULL strings “” “”
Unequal Strings
• Two different equal strings of arbitrary length
o Two strings with different length “cat” “mouse”
o Two strings of same length “cat” “dog”
• Check for case sensitivity
o Same strings with different characters capitalized
“Cat” “caT”
• One string is empty
Sequence
Sequence depicts programming instructions that do not have branching or any control
information. So we lump together several sequential instructions in one node of the
graph.
If
Second structural form is the If statement. In the following graph, the first node at the left
depicts the if statement and the two nodes next to the first node correspond to the
successful case (if condition is true) and unsuccessful case (if condition is false)
consecutively. The control comes to the same instruction from either of these
intermediate instructions.
Case
In Case statement, control can take either of several branches (as opposed to only two in
If statement.) First node represents the switch statement (C/C++) and nodes in middle
correspond to all different cases. Program can take one branch and result into the same
instruction.
While
A while loop structure consists of a loop guard instruction through which the iteration in
the loop is controlled. The control keeps iterating in the loop as long as the loop guard
condition is true. It branches to the last instruction when it becomes false.
sorted = false; 6 1
while (!sorted) { //1
sorted = true; 2
for (i=0; i < N-1; i++) { //2
if a[i] > a[i+1] {
3
swap(a[i], a[i+1]); //3
sorted = false;
} //4 4
} //5
} //6 5
Paths
Following are possible paths from starting to the end of this code.
Path1: 1-6
Path2: 1-2-3-4-5-1-6
Path3: 1-2-4-5-1-6
Path4: 1-2-4-2-3-4-5-6-1
Coverage
• Statement Coverage: In this scheme, statements of the code are tested for a
successful test that checks all the statements lying on the path of a successful
scenario.
• Branch Coverage: In this scheme, all the possible branches of decision structures
are tested. Therefore, sequences of statements following a decision are tested.
• Path Coverage: In path coverage, all possible paths of a program from input
instruction to the output instruction are tested. An exhaustive list of test cases is
generated and tested against the code.
if (a = = b) //1 1
c = d; //2
2
a++; //3
3
Statement coverage:
• a=1,b=1
• If a==b then statement 2, statement 3
Branch coverage
• Statement 1: two braches 1-2, 1-3
• Test case 1: if a =1, b=1 then statement 2
• Test case 2: if a=1,b=2: then statement 3
Path coverage
Same as branch testing
Paths
The following is an analysis of the above-mentioned code and the flow diagram. It
determines the number of paths against different iterations of the loop.
• N = 0: If the control does not enter into the loop then only one path will be
traversed. It is 1-5.
• N=1: Two different paths can possibly be traversed (depending on condition).
o 1-2-4-1-5
o 1-3-4-1-5
• N=2: Four possible paths can be traversed.
o 1-2-4-1-2-4-1-5
o 1-2-4-1-3-4-1-5
o 1-3-4-1-2-4-1-5
o 1-3-4-1-3-4-1-5
• Generalizing the relation between loop variable N and the number of possible
paths, for the value of N, 2N paths are possible
o Thus if N = 20 it means more then 1 million paths are possible.
Thus, the number of paths in a program that contains loops tends to infinity. It is
impossible to conduct exhaustive testing of a program that may consist of infinite number
of test cases. The question arises, how many test cases need to be executed in order to test
all the major scenarios in the code at least once? The answer is, calculate the cyclomatic
complexity of the code. This will give us a number that corresponds to the total number
of test cases that need to be generated in order to test all the statements and branches in
the code at least once.
Cyclomatic complexity
The concept of cyclomatic complexity is extremely useful in white box testing when
analyzing the relative complexity of the program to be tested. It revolves around
independent paths in a program which is any path through the program (from start to end)
that introduces at least one new set of processing statements or a new condition. An
independent path covers statements and branches of the code.
} //5
} //6 5
Cyclomatic complexity
• Number of edges = 8
• Number of nodes = 6
• C(G) = 8-6+2 = 4
Paths to be tested
• Path1: 1-6
• Path2: 1-2-3-4-5-1-6
• Path3: 1-2-4-5-1-6
• Path4: 1-2-4-2-3-4-5-6-1
Infeasible paths
Infeasible path is a path through a program which is never traversed for any input data.
Example
1
if (a == b) //1 2
c = c-1; //2
if (a != b) //3 3
c = c+1; //4
//5
4
In the above-mentioned example, there are two infeasible paths that will never be
traversed.
• Path1: 1-2-3-4-5
• Path2: 1-3-5
A good programming practice is such that minimize infeasible paths to zero. It will
reduce the number of test cases that need to be generated in order to test the application.
How can we minimize infeasible paths, by simply using else part with the if statement
and avoid program statements as given above.
1
if (a == b) //1
c = c-1; //2
else 2 3
c = c+1; //3
//4
4
There are no infeasible paths now!
Unit testing
A software program is made up of units that include procedures, functions, classes etc.
The unit testing process involves the developer in testing of these units. Unit testing is
roughly equivalent to chip-level testing for hardware in which each chip is tested
thoroughly after manufacturing. Similarly, unit testing is done to each module, in
isolation, to verify its behaviour. Typically the unit test will establish some sort of
artificial environment and then invoke routines in the module being tested. It then checks
the results returned against either some known value or against the results from previous
runs of the same test (regression testing). When the modules are assembled we can use
the same tests to test the system as a whole.
Software should be tested more like hardware, with
• Built-in self testing: such that each unit can be tested independently
• Internal diagnostics: diagnostics for program units should be defined.
• Test harness
The emphasis is on built in testability of the program units from the very beginning
where each piece should be tested thoroughly before trying to wire them together.
Quantitative Benefits
• Repeatable: Unit test cases can be repeated to verify that no unintended side
effects have occurred due to some modification in the code.
• Bounded: Narrow focus simplifies finding and fixing defects.
• Cheaper: Find and fix defects early
Qualitative Benefits
• Assessment-oriented: Writing the unit test forces us to deal with design issues -
cohesion, coupling.
• Confidence-building: We know what works at an early stage. Also easier to
change when it’s easy to retest.
result = squareRoot(argument);
assert (abs (result * result – argument) < epsilon);
Design Inspection
Code Inspection
Quality Assurance
Testing
Worst 30% 37% 50% 55% 65% 75% 77% 85% 95%
Median 40% 53% 65% 70% 80% 87% 90% 97% 99%
Best 50% 60% 75% 80% 87% 93% 95% 99% 99.9%
with the help of the above-mentioned table.
The above table depicts data that was published after analyzing 1500 projects. In these
projects, four different types of quality assurance mechanisms were employed. It is
evident from this table that testing alone can only remove 53% of defects. However,
testing and quality assurance mechanisms combine yield up to 65% efficiency. Whereas,
if we combine code inspection and testing together, results are up to 75%. Similarly,
design inspections and testing yield up to 80%. Moreover, combining design inspections,
quality assurance and testing results are up to 95%. If all four techniques are combined,
results are up to 99.9%.
Defect
Requirement Design Coding Origination
Documentatio Testing Maintenance
n
Defect origination
In inspections the emphasis is on early detection and fixing of defects from the program.
Following are the points in a development life cycle where defects enter into the
program.
• Requirements
• Design
• Coding
• User documentation
Inspection pre-conditions
A precise specification must be available before inspections. Team members must be
familiar with the organization standards. In addition to it, syntactically correct code must
be available to the inspectors. Inspectors should prepare a checklist that can help them
during the inspection process.
Inspection checklists.
Checklist of common errors in a program should be developed and used to drive the
inspection process. These error checklists are programming language dependent such that
the inspector has to analyze major constructs of the programming language and develop
checklists to verify code that is written using these checklists. For example, in a language
of weak type checking, one can expect a number of peculiarities in code that should be
verified. So the corresponding checklist can be larger. Other example of programming
language dependant defects are defects in variable initialization, constant naming, loop
termination, array bounds, etc.
An Inspection Checklist
Following is an example of an inspection checklist.
Exception • Have all possible error conditions been taken into account?
management
faults
Fault Class • Inspection Check
Data faults • Are all program variables initialized before their values are used?
• Have all constants been named?
• Should the lower bound of arrays be 0, 1, or something else?
• Should the upper bound of arrays be size or size -1?
• If character strings are used, is a delimiter explicitly assigned?
Control faults • For each conditional statement, is the condition correct?
• Is each loop certain to terminate?
• Are compound statements correctly bracketed?
• In case statements, are all possible cases accounted for?
Input/Output • Are all input variables used?
faults • Are all output variables assigned a value before they are output?
Interface faults • Do all function and procedure calls have correct number of parameters?
• Do formal and actual parameters types match?
• Are the parameters in right order?
• If components access shared memory, do they have the same model of
shared memory structure?
Storage • If a linked structure is modified, have all links been correctly assigned?
management • If dynamic storage is used, has space been allocated correctly?
faults • Is space explicitly de-allocated after it is no longer required?
In the checklist mentioned above, a number of fault classes have been specified and their
corresponding inspection checks are described in the column at the right side. This type
of checklist helps an inspector to look for specific defects in the program. These
inspection checks are the outcomes of experience that the inspector has gained out of
developing or testing similar programs.
Static analyzers
Static analyzers are software tools for source text processing. They parse the program text
and try to discover potentially erroneous conditions and bring these to the attention of the
verification and validation team. These tools are very effective as an aid to inspections.
But these are supplement to but not a replacement for inspections.