0% found this document useful (0 votes)
7 views38 pages

Software Development Life Cycle Overview

software testing and quality assurance module 1 for BCA students under university of Calicut

Uploaded by

jomoltjoy1999
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views38 pages

Software Development Life Cycle Overview

software testing and quality assurance module 1 for BCA students under university of Calicut

Uploaded by

jomoltjoy1999
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd

Module 1

Software Development Life Cycle Models Life Cycle Models

In this chapter—

 Phases of software project


 Quality, quality assurance, and quality control
 Testing, verification, and validation
 Process model to represent different phases
 Life cycle models

2.1 PHASES OF SOFTWARE PROJECT

A software project is made up of a series of phases. Broadly, most software projects comprise the
following phases.

 Requirements gathering and analysis


 Planning
 Design
 Development or coding
 Testing
 Deployment and maintenance

2.1.1 Requirements Gathering and Analysis

During requirements gathering, the specific requirements of the software to be built are gathered and
documented. If the software is bespoke software, then there is a single customer who can give these
requirements. If the product is a general-purpose software, then a product marketing team within the
software product organization specifies the requirements by aggregating the requirements of multiple
potential customers. In either case, it is important to ensure that the right requirements are captured at
every stage. The requirements get documented in the form of a System Requirements Specification (SRS)
document. This document acts as a bridge between the customer and the designers chartered to build the
product.

2.1.2 Planning

The purpose of the planning phase is to come up with a schedule, the scope, and resource requirements
for a release. A plan explains how the requirements will be met and by which time. It needs to take into
account the requirements—what will be met and what will not be met—for the current release to decide
on the scope for the project, look at resource availability, and to come out with set of milestones and
1
release date for the project. The planning phase is applicable for both development and testing activities.
At the end of this phase, both project plan and test plan documents are delivered.

2.1.3 Design

Design acts as a blueprint for the actual coding to proceed .The purpose of the design phase is to figure
out how to satisfy the requirements enumerated in the System Requirements Specification document.
The design phase produces a representation that will be used by the following phase, the development
phase. This representation should serve two purposes. First, from this representation, it should be
possible to verify that all the requirements are satisfied. Second, this representation should give
sufficient information for the development phase to proceed with the coding and implementation of
the system. Design is usually split into two levels—high-level design and low-level or a detailed design.
The design step produces the system design description (SDD) document that will be used by
development teams to produce the programs that realize the design.

2.1.4 Development or Coding

This development or coding phase comprises coding the programs in the chosen programming
language. It produces the software that meets the requirements the design was meant to satisfy. In
addition to programming, this phase also involves the creation of product documentation.

2.1.5 Testing

As the programs are coded (in the chosen programming language), they are also tested. In addition, after
the coding is (deemed) complete, the product is subjected to testing. Testing is the process of exercising
the software product in pre-defined ways to check if the behavior is the same as expected behavior.
By testing the product, an organization identifies and removes as many defects as possible before
shipping it out.

2.1.6 Deployment and Maintenance

Once a product is tested, it is given to the customers who deploy it in their environments. As the users
start using the product in their environments, they may observe discrepancies between the actual behavior
of the product and what they were given to expect (either by the marketing people or through the product
documentation). Such discrepancies could end up as product defects, which need to be corrected. The
product now enters the maintenance phase, wherein the product is maintained or changed to satisfy the
changes that arise from customer expectations, environmental changes, etc. Maintenance is made up
of corrective maintenance (for example, fixing customer-reported problems), adaptive maintenance (for
example, making the software run on a new version of an operating system or database), and preventive
maintenance (for example, changing the application program code to avoid a potential security hole in an
operating system code).

2
.2 QUALITY, QUALITY ASSURANCE, AND QUALITY CONTROL

A software product is designed to satisfy certain requirements of a given customer (or set of customers).
How can we characterize this phrase—“satisfying requirements”? Requirements get translated into
software features, each feature being designed to meet one or more of the requirements. For each such
feature, the expected behavior is characterized by a set of test cases. Each test case is further
characterized by

Quality is meeting the requirements expected of the software, consistently and predictably.

1. The environment under which the test case is to be executed;


2. Inputs that should be provided for that test case;
3. How these inputs should get processed;
4. What changes should be produced in the internal state or environment; and
5. What outputs should be produced.

The actual behavior of given software for a given test case, under a given set of inputs, in a given
environment, and in a given internal state is characterized by

1. How these inputs actually get processed;


2. What changes are actually produced in the internal state or environment; and
3. What outputs are actually produced.

Passed : If the actual behavior and the expected behavior are identical in all their characteristics, then
that test case is said to be passed. Failed/ Defect: If not, the given software is said to have a defect on
that test case.

How do we increase the chances of a product meeting the requirements expected of it, consistently and
predictably? There are two types of methods—

1. Quality control (after) -concentrate product rather than process


2. Quality assurance (before) – concentrate process rather than product

Quality control : attempts to build a product, test it for expected behavior after it is built, and if the
expected behavior is not the same as the actual behavior of the product, fixes the product as is necessary
and rebuilds the product. This iteration is repeated till the expected behavior of the product matches the
actual behavior for the scenarios tested. Thus quality control is defect-detection and defect-correction
oriented, and works on the product rather than on the process.

Quality assurance: on the other hand, attempts defect prevention by concentrating on the process of
producing the product rather than working on defect detection/correction after the product is built. For
example, instead of producing and then testing a program code for proper behavior by exercising the built
product, a quality assurance approach would be to first review the design before the product is built and
correct the design errors in the first place. Similarly, to ensure the production of a better code, a quality
assurance process may mandate coding standards to be followed by all programmers. As can be seen from
the above examples, quality assurance normally tends to apply to all the products that use a process. Also,

3
since quality assurance continues throughout the life of the product it is everybody's responsibility; hence
it is a staff function. In contrast, the responsibility for quality control is usually localized to a quality
control team.

Table 2.1 summarizes the key distinctions between quality control and quality assurance.

Table 2.1 Difference between quality assurance and quality control.

Quality Control (after) Quality Assurance (before)

Concentrates on specific products Concentrates on the process of producing the


products

Defect-detection and correction oriented Defect-prevention oriented

Usually done after the product is built Usually done throughout the life cycle

This is usually a line function This is usually a staff function

Examples: software testing at various levels Examples: reviews and audits

2.3 TESTING, VERIFICATION, AND VALIDATION

The purpose of testing is to uncover defects in the system (and to have someone fix the defects). Testing
is done by a set of people within a software product (or service) organization whose goal and charter is to
uncover the defects in the product before it reaches the customer. As we saw in the previous chapter, the
purpose of testing is NOT to prove that the product has no defects. The purpose of software testing
is to find defects in a software product.

Verification (QA) (before) (prevent) is the process of evaluating a system or component to determine
whether the products of a given phase satisfy the conditions imposed at the start of that phase. “ Are we
building the product right ?” [Proactive]

Validation(QC) (after) (detect) is the process of evaluating a system or component during or at the
end of the development process to determine whether it satisfies specified requirements. “ Are we
building right product ?” [Reactive]

To build the product right, certain activities/conditions/procedures are imposed at the beginning of the life
cycle. These activities are considered “proactive” as their purpose is to prevent the defects before they
4
take shape. The process activities carried out during various phases for each of the product releases can be
termed as verification. Requirements review, design review, and code review are some examples of
verification activities.

To build the right product, certain activities are carried out during various phases to validate whether the
product is built as per specifications. These activities are considered “reactive” as their purpose is to find
defects that affect the product and fix them as soon as they are introduced. Some examples of validation
include unit testing performed to verify if the code logic works, integration testing performed to verify the
design, and system testing performed to verify that the requirements are met.

Quality Assurance = Verification

Quality Control = Validation = Testing

To summarize, there are different terminologies that may stand for the same or similar concepts. For all
practical purposes in this book, we can assume verification and quality assurance to be one and the same.
Similarly quality control, validation, and testing mean the same.

2.4 PROCESS MODEL TO REPRESENT DIFFERENT PHASES

A process model is a way to represent any given phase of software development that effectively builds in
the concepts of validation and verification to prevent and minimize the delay between defect injection and
defect detection (and eventual correction). In this model, each phase of a software project is characterized
by the following.

This model, known as the Entry Task Verification eXit or ETVX model, offers several advantages for
effective verification and validation.

 Entry criteria, which specify when that phase can be started. Also included are the inputs for
the phase.
 Tasks, or steps that need to be carried out in that phase, along with measurements that
characterize the tasks.
 Verification, which specifies methods of checking that the tasks have been carried out
correctly.
 Exit criteria, which stipulate the conditions under which one can consider the phase as done.
Also included are the outputs for only the phase.

1. Clear entry criteria make sure that a given phase does not start prematurely.
2. The verification for each phase (or each activity in each phase) helps prevent defects, or at
least, minimizes the time delay between defect injection and defect detection.

5
3. Documentation of the detailed tasks that comprise each phase reduces the ambiguity in
interpretation of the instructions and thus minimizes the variations that can come from
repeated executions of these tasks by different individuals.
4. Clear exit criteria provide a means of validation of the phase, after the phase is done but before
handing over to the next phase.

An example of applying the ETVX model to the design phase is presented in Figure 2.1.

Figure 2.1 ETVX model applied to design.

2.5 LIFE CYCLE MODELS

The ETVX model characterizes a phase of a project. A Life Cycle model describes how the phases
combine together to form a complete project or life cycle. Such a model is characterized by the following
attributes.

The activities performed In any given software project, apart from the most common activities or
phases—requirements gathering, design, development, testing, and maintenance—there could be other
activities as well. Some of these activities could be technical activities (for example, porting) and some
could be non-technical (for example, hiring).

The deliverables from each activity Each activity produces a set of deliverables, which are the end
products of that activity. For example, the requirements gathering phase produces the SRS document, the
design phase produces the SDD document, and so on.

6
Methods of validation of the deliverables The outputs produced by a given activity represent the goal
to be satisfied by that activity. Hence it is necessary to have proper validation criteria for each output.

The sequence of activities The different activities work together in unison in a certain sequence of
steps to achieve overall project goals. For example, the process of requirements gathering may involve
steps such as interviews with customers, documentation of requirements, validation of documented
requirements with customers, and freezing of requirements. These steps may be repeated as many times
as needed to get the final frozen requirements.

Methods of verification of each activity, including the mechanism of communication amongst the
activities The different activities interact with one another by means of communication methods. For
example, when a defect is found in one activity and is traced back to the causes in an earlier activity,
proper verification methods are needed to retrace steps from the point of defect to the cause of the defect.

We will now look at some of the common life cycle models that are used in software projects. For each
model, we will look at:

1. a brief description of the model;


2. the relationship of the model to verification and validation activities; and
3. typical scenarios where that life cycle model is useful.

2.5.1 Waterfall Model

In the Waterfall model, a project is divided into a set of phases (or activities). Each phase is distinct, that
is, there are clear lines of separation between the phases, with very clear demarcation of the functions of
each of the phases. A project starts with an initial phase, and upon completion of the phase, moves on to
the next phase. On the completion of this phase, the project moves to the subsequent phase and so on.
Thus the phases are strictly time sequenced.

We depict one example of a project in the Waterfall model in Figure 2.2. The project goes through a
phase of requirements gathering. At the end of requirements gathering, a System Requirements
Specification document is produced. This becomes the input to the design phase. During the design phase,
a detailed design is produced in the form of a System Design Description. With the SDD as input, the
project proceeds to the development or coding phase, wherein programmers develop the programs
required to satisfy the design. Once the programmers complete their coding tasks, they hand the product

7
to the testing team, who test the product before it is released.

Figure 2.2 Waterfall model.

If there is no problem in a given phase, then this method can work, going in one direction (like a
waterfall). But what would happen if there are problems after going to a particular phase? For example,
you go into the design phase and find that it is not possible to satisfy the requirements, going by the
current design approach being used. What could be the possible causes and remedies? You may try an
alternative design if possible and see if that can satisfy the requirements. If there are no alternative design
approaches possible, then there must be feedback to the requirements phase to correct the requirements.

A Waterfall model is characterized by three attributes.

1. The project is divided into separate, distinct phases.


8
2. Each phase communicates to the next through pre-specified outputs.
3. When an error is detected, it is traced back to one previous phase at a time, until it gets
resolved at some earlier phase.

Let us take the example one step further. Suppose a design was created for a given set of requirements
and the project passed on to the programming/development phase. At this point of time, it was found that
it was not possible to develop the programs because of some limitations. What would you do? One
approach would be to try out alternative strategies in the development phase so that the design could still
be satisfied. Another possibility could be that there are flaws in design that cause conflicts during
development and hence the design has to be revisited. When the design phase is revisited—like in the
previous case—it may happen that the problem may have to be addressed in the requirements phase itself.
So, a problem in one phase could potentially be traced back to any of the previous phases.

Since each phase has an output, the latter can be validated against a set of criteria. To increase the
effectiveness, the completion criteria for each output can be published a priori. Before a phase starts, the
completion criteria for the previous phase can be checked and this can act as a verification mechanism for
the phase. This can minimize the kind of delays we discussed in the example above.

The main strength of the Waterfall Model is its simplicity. The model is very useful when a project can
actually be divided into watertight compartments. But very few software projects can be divided thus. The
major drawback in the Waterfall model arises from the delay in feedback among the phases, and thus the
ineffectiveness of verification and validation activities. An error in one phase is not detected till at least
the next phase. When a given phase detects an error, the communication is only to the immediately
preceding phase. This sequential nature of communication among the phases can introduce inordinate
delays in resolving the problem. The reduced responsiveness that is inherent in the model and the fact that
the segregation of phases is unrealistic severely restricts the applicability of this model.

2.5.2 Prototyping and Rapid Application Development Models

Prototyping and Rapid Application Development (RAD) models recognize and address the following
issues.

1. Early and frequent user feedback will increase the chances of a software project meeting the
customers’ requirements.
2. Changes are unavoidable and the software development process must be able to adapt itself to
rapid changes.

1. A Prototyping model uses constant user interaction, early in the requirements


gathering stage, to produce a prototype.
2. The proto-type is used to derive the system requirements specification and can be
discarded after the SRS is built.
3. An appropriate life cycle model is chosen for building the actual product after the user
accepts the SRS.

The Prototyping model comprises the following activities.

9
1. The software development organization interacts with customers to understand their
requirements.
2. The software development organization produces a prototype to show how the eventual
software system would look like. This prototype would have the models of how the input
screens and output reports would look like, in addition to having some “empty can
functionality” to demonstrate the workflow and processing logic.
3. The customer and the development organization review the prototype frequently so that the
customer's feedback is taken very early in the cycle (that is, during the requirements gathering
phase).
4. Based on the feedback and the prototype that is produced, the software development
organization produces the System Requirements Specification document.
5. Once the SRS document is produced, the prototype can be discarded.
6. The SRS document is used as the basis for further design and development.

Thus, the prototype is simply used as a means of quickly gathering (the right) requirements. This model
has built-in mechanisms for verification and validation of the requirements. As the prototype is being
developed, the customer's frequent feedback acts as a validation mechanism. Once the SRS is produced, it
acts as the verification mechanism for the design and subsequent steps. But the verification and validation
activities of the subsequent phases are actually dictated by the life cycle model that is followed after the
SRS is obtained.

This model is obviously advantageous when a customer can participate by giving feedback. This model is
also useful in cases where the feedback can be easily quantified and incorporated, for example,
determining user interface, predicting performance, and so on.

For a general-purpose product, which is meant for many customers, there is no single customer whose
feedback can be taken as final. In these cases, a product manager in the marketing group of the product
vendor usually plays the role of the eventual customer. Hence the applicability of this model is somewhat
limited to general-purpose products. Furthermore, the prototype is used as a means of capturing
requirements and is not necessarily meant to be used afterwards. Oftentimes, the prototype (or parts of the
prototype) makes its way to becoming the product itself. This can have undesirable effects as the
prototype usually employs several short cuts, unstructured methods, and tools to achieve a quick
turnaround. Such short cuts are potential sources of defects in live environments and thus can place a
heavy burden on maintenance and testing.

The Rapid Application Development model is a variation of the Prototyping Model. Like the Prototyping
Model, the RAD Model relies on feedback and interaction by the customers to gather the initial
requirements. However, the Prototyping model differs from the RAD Model on two counts.

First, in the RAD Model, it is not a prototype that is built but the actual product itself. That is, the built
application (prototype, in the previous model) is not discarded. Hence, it is named Rapid Application
Development model.

Second, in order to ensure formalism in capturing the requirements and proper reflection of the
requirements in the design and subsequent phases, a Computer Aided Software Engineering (CASE)
tool is used throughout the life cycle, right from requirements gathering. Such CASE tools have
10
 methodologies to elicit requirements;
 repositories to store the gathered requirements and all downstream entities such as design
objects; and
 mechanisms to automatically translate the requirements stored in the repositories to design and
generate the code in the chosen programming environment.

The methodologies provided by a CASE tool can provide inbuilt means of verification and validation. For
example, the tool may be able to automatically detect and resolve inconsistencies in data types or
dependencies. Since the design (and, perhaps, even the program code) can be automatically generated
from the requirements, the validation can be very complete, extending to all the downstream phases,
unlike the Prototyping model.

This method can have wider applicability for even general-purpose products. The automatic generation of
the design and programs produced by a CASE tool makes this model more attractive. The cost of such
CASE tools is a factor that an organization would have to consider before deciding on the use of this
model for a given project. In addition, CASE tools and this model is generally more suited for
applications projects rather than systems type projects.

2.5.3 Spiral or Iterative Model

The Spiral or Iterative model follows a process in which the requirements gathering, design, coding, and
testing are performed iteratively till all requirements are met. There is also a good amount of overlap
among the activities of requirements gathering, design, coding, and testing following this model. What
phase the product is in is difficult to conclude as each requirement can be at a different phase. The only
conclusion that can be made is at what phase each of the requirements is in. If a defect is produced in any
phase of a given requirement, it may cause that requirement to revisit an earlier phase. This model enables
incremental development whereby the product evolves, with requirements getting added to it
dynamically. This enables the product to be demonstrated, at any point of time, with the functionality
available at that point of time. It also enables the “increments” to be sent to the customer for approval.
The progress of the product can be seen from the beginning of the project as the model delivers
“increments” at regular intervals. Even though it will be very difficult to plan a release date following this
model, it allows the progress to be tracked and the customer approvals to be obtained at regular intervals,
thereby reducing the risk of finding major defects at a later point of time. Table 2.2 gives an example of
phases for some of the requirements in the product.

Table 2.2 Some product requirements and phases.

Requirements Status/Phase currently in

Requirement-1 Coding

Requirement-2 Design

11
Requirements Status/Phase currently in

Requirement-3 Requirement

Requirement-4 Testing

Requirement-5 Released

Figure 2.3 (the coloured figure is available on Illustrations) depicts the Spiral model and the phases
involved in the model, for the example on Table 2.2. As can be seen, each requirement is “spiraling
outwards” through the different phases as the entire project evolves.

Figure 2.3 Spiral model.

2.5.4 The V Model

The Waterfall Model viewed testing as a post-development (that is, post-coding) activity. The Spiral
Model took this one step further and tried to break up the product into increments each of which can be
tested separately. The V Model starts off being similar to the Waterfall Model in that it envisages product
development to be made up of a number of phases or levels. However, the new perspective that the V
Model brings in is that different types of testing apply at different levels. Thus, from a testing perspective,
the type of tests that need to be done at each level vary significantly.

12
For instance, consider a typical product development activity represented as a Waterfall Model earlier
in Figure 2.2. The system starts with the overall business requirements from the point of view of
customers. These requirements cover hardware, software, and operational requirements. Since our focus
is on the software, moving from overall requirements to software requirements becomes the next step. In
order to realize the software requirements, the proposed software system is envisaged as a set of
subsystems that work together. This high-level design (of breaking the system into subsystems with
identified interfaces) then gets translated to a more detailed or low-level design. This detailed design goes
into issues like data structures, algorithm choices, table layouts, processing logic, exception conditions,
and so on. It results in the identification of a number of components, each component realized by program
code written in appropriate programming languages.

Given these levels, what kind of tests apply in each of these levels? To begin with, for overall business
requirements, eventually whatever software is developed should fit into and work in this overall context
and should be accepted by the end users, in their environment. This testing, the final proof of the pudding,
is acceptance testing. But, before the product is deployed in the customer's environment, the product
vendor should test it as an entire unit to make sure that all the software requirements are satisfied by the
product that is developed. This testing of the entire software system can be called system testing. Since
high-level design views the system as being made up of interoperating and integrated (software)
subsystems, the individual subsystems should be integrated and tested together before a full blown system
test can be done. This testing of high-level design corresponds to integration testing. The components that
are the outputs of the low-level design have to be tested independently before being integrated. Thus, the
testing corresponding to the low-level design phase is component testing. Finally, since coding produces
several program units, each of these smaller program units have to be tested independently before trying
to combine them together to form components. This testing of the program units forms unit testing.

Figure 2.4 depicts the different types of testing that apply to each of the steps. For simplicity, we have not
shown the planning phase as a separate entity since it is common for all testing phases. But, it is not
possible to execute any of these tests until the product is actually built. In other words, the step called
“testing” is now broken down into different sub-steps called acceptance testing, system testing, and so on
as shown in Figure 2.4. So, it is still the case that all the testing execution related activities are done only
at the end of the life cycle.

13
Figure 2.4 Phases of testing for different development phases.

Even though the execution of the tests cannot be done till the product is built, the design of tests can be
carried out much earlier. In fact, if we look at the aspect of skill sets required for designing each type of
tests, the people best suited to design each of these tests are those who are actually performing the
function of creating the corresponding artifact. For example, the best people to articulate what the
acceptance tests should be are the ones who formulate the overall business requirements (and, of course,
the customers, where possible). Similarly, the people best equipped to design the integration tests are
those who know how the system is broken into subsystems and what the interfaces between the
subsystems are—that is, those who perform the high-level design. Again, the people doing development
know the innards of the program code and thus are best equipped to design the unit tests.

1. The V-model splits testing into two parts—design and execution.


2. Test design is done early, while test execution is done in the end.
3. There are different types of tests for each phase of life cycle.

Not only are the skill sets required for designing these different types of tests different, but also, there is
no reason to defer the designing of the tests till the very end. As and when each activity on the left-hand
side of the “V” is being carried out, the design of the corresponding type of tests can be carried out. By

14
performing an early design of the tests and deferring only the test execution till the end, we achieve three
important gains.

 First, we achieve more parallelism and reduce the end-of-cycle time taken for testing.
 Second, by designing tests for each activity upfront, we are building in better upfront
validation, thus again reducing last-minute surprises.
 Third, tests are designed by people with appropriate skill sets.

This is the basis for the V Model, which presents excellent advantages for verification and validation. As
shown in Figure 2.5, for each type of test, we move the design of tests upstream, along with the actual
activities and retain the test execution downstream, after the product is built.

Figure 2.5 V Model.

2.5.5 Modified V Model

The V Model split the design and execution portion of the various types of tests and attached the test
design portion to the corresponding earlier phases of the software life cycle.

An assumption made there was that even though the activity of test execution was split into execution of
tests of different types, the execution cannot happen until the entire product is built. For a given product,
the different units and components can be in different stages of evolution. For example, one unit could be
still under development and thus be in the unit-testing phase whereas another unit could be ready for
component testing while the component itself may not be ready for integration testing. There may be
15
components that are ready (that is, component tested) for integration and being subjected to integration
tests (along with other modules which are also ready for integration, provided those modules can be
integrated). The V Model does not explicitly address this natural parallelism commonly found in product
development.

In the modified V Model, this parallelism is exploited. When each unit or component or module is given
explicit exit criteria to pass on to the subsequent stage, the units or components or modules that satisfy a
given phase of testing move to the next phase of testing where possible, without necessarily waiting for
all the units or components or modules to move in unison from one phase of testing to another, as shown
in Figure 2.6.

Figure 2.6 Modified V Model.

1. The modified V model recognizes that different parts of a product are in different
stages of evolution.
2. Each part enters the appropriate testing phase (such as unit testing, component testing,
and so on) when the appropriate entry criteria are met.

Just as the V Model introduced various types of testing, the modified V model introduces
various phases of testing. A phase of testing has a one-to-one mapping to the types of testing, that is,
there is a unit-testing phase, component-testing phase, and so on. Once a unit has completed the unit-
testing phase, it becomes part of a component and enters the component-testing phase. It then moves to
integration-testing phase and so on. Rather than view the product as going through different types of tests
(as the V model does), the modified V Model views each part of the product to go through different
phases of testing. These are actually two sides of the same coin and thus provide complimentary views.
The main advantage the modified V model brings to the table is the recognition of the parallelism present
in different parts of the product and assigning each part to the most appropriate phase of testing that is

16
possible. In Figure 2.6, the columns of the table represents one side of V, and rows (which are test phases)
represent the other side of V.

In Figure 2.6, notice that different phases of testing are done in parallel. While starting a phase of testing
it is important to look at whether the product is ready for testing. It is determined by a set of entry
criteria. The earliest possible quality to start the next phase of testing is denoted by entry criteria, and to
start the next phase of testing the earlier phase need not have completed. The testing phases are also
associated with a set of exit criteria to complete the test activities for each phase. They are determined
by exit criteria. The entry and exit criteria for each of the phases ensure that right quality of product
delivered for starting the test and right amount of testing is completed for the release. Even though it is
indicated in the picture all of the test phases finish at the same time, practically it can have different time
lines. The longest phase determines the release date.

In Figure 2.6, there are two additional activities that have not been discussed before. The coloured figure
is available on Illustrations. These are “Component (1, 2…) Complete” and “Components Complete”;
these are not additional phases in a life cycle. They have been introduced just to denote that integration
testing can start after two components have been completed, and when all components are integrated and
tested, the next phase of testing, that is, system testing can start.

2.5.6 Comparison of Various Life Cycle Models

As can be seen from the above discussion, each of the models has its advantages and disadvantages. Each
of them has applicability in a specific scenario. Each of them also provides different issues, challenges,
and opportunities for verification and validation. We summarize in Table 2.3 the salient points about
applicability and relevance to verification and validation for each of the models.

Table 2.3 Model applicability and relevance to verification and validation.

Models Where Applicable Relevant Verification and Validation (V & V) Issues

Waterfall Where very clearly demarcated Testing / V & V postponed by at least one phase
phases are present
When deliverables of each phase Typically testing is among the most downstream activities
can be frozen beforeproceeding to
the next phase Communication of error (and hence time for correction) can
be high

Prototyping Where we have a user (or a product Provides inbuilt feedback for the requirements
manager) who can give feedback
Reuse of prototype (instead of throwing it away) can make
verification and validation difficult and may produce
undesirable effects

RAD Where we have a user (or a product Built-in feedback available beyondrequirements also

17
Models Where Applicable Relevant Verification and Validation (V & V) Issues

manager) who can give feedback


CASE tools can generate useful documentation that further
When we have CASE and other enhances V & V
modeling tools

Spiral Products evolving as increments Extends V & V to all increments

Intermediate checking and Extends V & V to all phases (that is, those beyond
correction is possible requirements gathering as well)

Enables the products to be demonstrated at any phase and


enables frequent releases

V model When design of tests can be Early design of tests reduces overall delay by increasing
separated from the actual execution parallelism between development and testingEarly design of
tests enables better and more timely validation of individual
phases

Modified V When a product can be broken Parallelism of V model further increased by making each
model down into different parts, each of part evolve independently
which evolves independently
Further reduces the overall delay by introducing parallelism
between testing activities

Module 1.2 White Box Testing

In this chapter—

 What is white box testing?


 Static testing
 Structural testing
 Challenges in white box testing

3.1 WHAT IS WHITE BOX TESTING?

Every software product is realized by means of a program code. White box testing is a way of testing the
external functionality of the code by examining and testing the program code that realizes the external
functionality. This is also known as clear box, or glass box or open box testing.

18
White box testing takes into account the program code, code structure, and internal design flow . In
contrast, black box testing, to be discussed in does not look at the program code but looks at the product
from an external perspective.

A number of defects come about because of incorrect translation of requirements and design into program
code. Some other defects are created by programming errors and programming language idiosyncrasies.
The different methods of white box testing discussed in this chapter can help reduce the delay between
the injection of a defect in the program code and its detection. Furthermore, since the program code
represents what the product actually does (rather than what the product is intended to do), testing by
looking at the program code makes us get closer to what the product is actually doing.

As shown in Figure 3.1, white box testing is classified into

1. “static” and
2. “structural” testing.

Figure 3.1 Classification of white box testing.

3.2 STATIC TESTING (ONLY SOURCE CODE )

Static testing is a type of testing which requires only the source code of the product, not the
binaries or executables. Static testing does not involve executing the programs on computers but involves
select people going through the code to find out whether

19
 the code works according to the functional requirement;
Specialist
 the code has been written in accordance with the design developed earlier in the project
life cycle;
in that
 the code for any functionality has been missed out; programm
 the code handles errors properly. ing
Static testing can be done by humans or with the help of specialized tools.

3.2.1 Static Testing by Humans

These methods rely on the principle of humans reading the program code to detect errors rather than
computers executing the code to find errors. This process has several advantages.

1. Sometimes humans can find errors that computers cannot. For example, when there are two
variables with similar names and the programmer used a “wrong” variable by mistake in an
expression, the computer will not detect the error but execute the statement and produce incorrect
results, whereas a human being can spot such an error.
2. By making multiple humans read and evaluate the program, we can get multiple perspectives and
therefore have more problems identified upfront than a computer could.
3. A human evaluation of the code can compare it against the specifications or design and thus
ensure that it does what is intended to do. This may not always be possible when a computer runs
a test.
4. A human evaluation can detect many problems at one go and can even try to identify the root
causes of the problems. More often than not, multiple problems can get fixed by attending to the
same root cause. Typically, in a reactive testing, a test uncovers one problem (or, at best, a few
problems) at a time. Often, such testing only reveals the symptoms rather than the root causes.
Thus, the overall time required to fix all the problems can be reduced substantially by a human
evaluation.
5. By making humans test the code before execution, computer resources can be saved. Of course,
this comes at the expense of human resources.
6. A proactive method of testing like static testing minimizes the delay in identification of the
problems. As we have seen in Chapter 1, the sooner a defect is identified and corrected, lesser is
the cost of fixing the defect.
7. From a psychological point of view, finding defects later in the cycle (for example, after the code
is compiled and the system is being put together) creates immense pressure on programmers.
They have to fix defects with less time to spare. With this kind of pressure, there are higher
chances of other defects creeping in.

There are multiple methods to achieve static testing by humans. They are (in the increasing order of
formalism) as follows.

20
1. Desk checking of the code- author of the code
2. Code walkthrough- group oriented –question arises
3. Code review
4. Code inspection Formal inspection

Since static testing by humans is done before the code is compiled and executed, some of these methods
can be viewed as process-oriented or defect prevention-oriented or quality assurance-oriented activities
rather than pure testing activities. Especially as the methods become increasingly formal (for example,
Fagan Inspection), these traditionally fall under the “process” domain. They find a place in formal process
models such as ISO 9001, CMMI, and so on and are seldom treated as part of the “testing” domain.
Nevertheless, as mentioned earlier in this book, we take a holistic view of “testing” as anything that
furthers the quality of a product. These methods have been included in this chapter because they have
visibility into the program code.

We will now look into each of these methods in more detail.

Note : Capability Maturity Model Integration (CMMI) is a process level improvement training and
appraisal program

[Link] Desk checking

Normally done manually by the author of the code, desk checking is a method to verify the portions of
the code for correctness. In effect, this method relies completely on the author's thoroughness, diligence,
and skills .Such verification is done by comparing the code with the design or specifications to make sure
that the code does what it is supposed to do and effectively. This is the desk checking that most
programmers do before compiling and executing the code. Whenever errors are found, the author applies
the corrections for errors on the spot. This method of catching and correcting errors is characterized by:

1. No structured method or formalism to ensure completeness and


2. No maintaining of a log or checklist.

The main advantage offered by this method is that the programmer who knows the code and the
programming language very well is well equipped to read and understand his or her own code. Also, since
this is done by one individual, there are fewer scheduling and logistics overheads. Furthermore, the
defects are detected and corrected with minimum time delay.

Some of the disadvantages of this method of testing are as follows.

1. A developer is not the best person to detect problems in his or her own code. He or she may be
tunnel visioned and have blind spots to certain types of problems.
2. Developers generally prefer to write new code rather than do any form of testing! (We will see
more details of this syndrome )

21
3. This method is essentially person-dependent and informal and thus may not work consistently
across all developers.

Owing to these disadvantages, the next two types of proactive methods are introduced. The basic
principle of walkthroughs and formal inspections is to involve multiple people in the review process.

[Link] Code walkthrough

This method and formal inspection (described in the next section) are group-oriented methods.
Walkthroughs are less formal than inspections. The line drawn in formalism between walkthroughs and
inspections is very thin and varies from organization to organization. The advantage that walkthrough has
over desk checking is that it brings multiple perspectives. In walkthroughs, a set of people look at the
program code and raise questions for the author. The author explains the logic of the code, and
answers the questions. If the author is unable to answer some questions, he or she then takes those
questions and finds their answers. Completeness is limited to the area where questions are raised by the
team.

[Link] Formal inspection (defect logging meeting)

Code inspection—also called Fagan Inspection (named after the original formulator)—is a method,
normally with a high degree of formalism. The focus of this method is to detect all faults, violations, and
other side-effects. This method increases the number of defects detected by

1. demanding thorough preparation before an inspection/review;


2. enlisting multiple diverse views;
3. assigning specific roles to the multiple participants; and
4. going sequentially through the code in a structured manner.

A formal inspection should take place only when the author has made sure the code is ready for
inspection by performing some basic desk checking and walkthroughs. When the code is in such a
reasonable state of readiness, an inspection meeting is arranged.

There are four roles in inspection.

1. First is the author of the code.


2. Second is a moderator who is expected to formally run the inspection according to the process.

3. Third are the inspectors. These are the people who actually provides, review comments for the
code. There are typically multiple inspectors.

4. Finally, there is a scribe, who takes detailed notes during the inspection meeting and circulates
them to the inspection team after the meeting.

The inspection team assembles at the agreed time for the inspection meeting (also called the defect
logging meeting). The moderator takes the team sequentially through the program code, asking each
inspector if there are any defects in that part of the code. If any of the inspectors raises a defect, then the

22
inspection team deliberates on the defect and, when agreed that there is a defect, classifies it in two
dimensions–

–minor/major and

systemic/mis-execution.

A mis-execution defect is one which, as the name suggests, happens because of an error or slip on the
part of the author. It is unlikely to be repeated later, either in this work product or in other work products.
An example of this is using a wrong variable in a statement. Systemic defects, on the other hand, can
require correction at a different level. For example, an error such as using some machine-specific
idiosyncrasies may have to removed by changing the coding standards. Similarly, minor defects are
defects that may not substantially affect a program, whereas major defects need immediate attention.

A scribe formally documents the defects found in the inspection meeting and the author takes care of
fixing these defects. In case the defects are severe, the team may optionally call for a review meeting to
inspect the fixes to ensure that they address the problems. In any case, defects found through inspection
need to be tracked till completion and someone in the team has to verify that the problems have been
fixed properly.

[Link] Combining various methods

Risk

1. High and Medium :- we can go with Formal inspection

2. Low:- Desk checking is enough

The methods discussed above are not mutually exclusive. They need to be used in a judicious
combination to be effective in achieving the goal of finding defects early.

Formal inspections have been found very effective in catching defects early. Some of the challenges to
watch out for in conducting formal inspections are as follows.

1. These are time consuming. Since the process calls for preparation as well as formal meetings,
these can take time.
2. The logistics and scheduling can become an issue since multiple people are involved.
3. It is not always possible to go through every line of code, with several parameters and their
combinations in mind to ensure the correctness of the logic, side-effects and appropriate error
handling. It may also not be necessary to subject the entire code to formal inspection.

In order to overcome the above challenges, it is necessary to identify, during the planning stages, which
parts of the code will be subject to formal inspections. Portions of code can be classified on the basis of
their criticality or complexity as “high,” “medium,” and “low.” High or medium complex critical code

23
should be subject to formal inspections, while those classified as “low” can be subject to either
walkthroughs or even desk checking.

Desk checking, walkthrough, review and inspection are not only used for code but can be used for all
other deliverables in the project life cycle such as documents, binaries, and media.

Best Static Code Analysis Tools Comparison

Here is the list of the top 10 Static Code Analysis Tools for Java, C++, C# and Python:

Raxis ,RIPS Technologies, PVS-Studio, Kiuwan, Kritika ,Gamma, DeepScan Reshift, CodeScene
Behavioral Code Analysis, Veracode, Code Compare,Fortify Static Code Analyzer, Parasoft

CODE REVIEW CHECKLIST

DATA ITEM DECLARATION RELATED

 Are the names of the variables meaningful?


 If the programming language allows mixed case names, are there variable names with
confusing use of lower case letters and capital letters?
 Are the variables initialized?
 Are there similar sounding names (especially words in singular and plural)? [These
could be possible causes of unintended errors.]
 Are all the common stru tures, constants, and flags to be used defined in a header file
rather than in each file separately?

DATA USAGE RELATED

 Are values of right data types being assigned to the variables?


 Is the access of data from any standard files, repositories, or databases done through
publicly supported interfaces?
 If pointers are used, are they initialized properly?
 Are bounds to array subscripts and pointers properly checked?
 Has the usage of similar-looking operators (for example,=and == or & and && in C)
checked?

CONTROL FLOW RELATED

 Are all the conditional paths reachable?


 Are all the individal conditions in a complex condition separately evaluated?
 If there is a nested IF statement, are the THEN and ELSE parts appropriately
delimited?

24
 In the case of a multi-way branch like SWITCH / CASE statement, is a default clause
provided? Are the breaks after each CASE appropriate?
 Is there any part of code that is unreachable?
 Are there any loops that will never execute?
 Are there any loops where the final condition will never be met and hence cause the
program to go into an infinite loop?
 What is the level of nesting of the conditional statements? Can the code be simplified
to reduce complexity?

STANDARDS RELATED

 Does the code follow the coding conventions of the organization?


 Does the code follow any coding conventions that are platform specific (for example,
GUI calls specific to Windows or Swing)

STYLE RELATED

 Are unhealthy programming constructs (for example, global variables in C, ALTER


statement in COBOL) being used in the program?
 Is there usage of specific idiosyncrasies of a particular machine architecture or a given
version of an underlying product (for example, using “undocumented” features)?
 Is sufficient attention being paid to readability issues like indentation of code?

MISCELLANEOUS

 Have you checked for memory leaks (for example, memory acquired but not explicitly
freed)?

DOCUMENTATION RELATED

 Is the code adequately documented, especially where the logic is complex or the
section of code is critical for product functioning?
 Is appropriate change history documented?
 Are the interfaces and the parameters thereof properly documented?

3.3 STRUCTURAL TESTING –ON COMPUTER ITSELF

25
Structural testing takes into account the code, code structure, internal design, and how they are coded.

The fundamental difference between structural testing and static testing is that in structural testing
tests are actually run by the computer on the built product, whereas in static testing, the product is
tested by humans using just the source code and not the executables or binaries.

Structural testing entails running the actual product against some predesigned test cases to exercise as
much of the code as possible or necessary. A given portion of the code is exercised if a test case causes
the program to execute that portion of the code when running the test.

As discussed at the beginning of this chapter, structural testing can be further classified into unit/code
functional testing, code coverage, and code complexity testing.

3.3.1 Unit/Code Functional Testing (debugging code )

This initial part of structural testing corresponds to some quick checks that a developer performs before
subjecting the code to more extensive code coverage testing or code complexity testing. This can happen
by several methods.

1. Initially, the developer can perform certain obvious tests, knowing the input variables and
the corresponding expected output variables. This can be a quick test that checks out any
obvious mistakes. By repeating these tests for multiple values of input variables, the
confidence level of the developer to go to the next level increases. This can even be done prior
to formal reviews of static testing so that the review mechanism does not waste time catching
obvious errors.
2. For modules with complex logic or conditions, the developer can build a “debug version” of
the product by putting intermediate print statements and making sure the program is
passing through the right loops and iterations the right number of times. It is important to
remove the intermediate print statements after the defects are fixed.

26
3. Another approach to do the initial test is to run the product under a debugger or an Integrated
Development Environment (IDE). These tools allow single stepping of instructions (allowing
the developer to stop at the end of each instruction, view or modify the contents of variables,
and so on), setting break points at any function or instruction, and viewing the various system
parameters or program variable values.

All the above fall more under the “debugging” category of activities than under the “testing”
category of activities. All the same, these are intimately related to the knowledge of code structure and
hence we have included these under the “white box testing” head. This is consistent with our view that
testing encompasses whatever it takes to detect and correct defects in a product.

3.3.2 Code Coverage Testing ( to find out the percentage of code covered by testing )

Since a product is realized in terms of program code, if we can run test cases to exercise the different
parts of the code, then that part of the product realized by the code gets tested. Code coverage testing
involves designing and executing test cases and finding out the percentage of code that is covered by
testing. The percentage of code covered by a test is found by adopting a technique
called instrumentation of code. There are specialized tools available to achieve instrumentation.
Instrumentation rebuilds the product, linking the product with a set of libraries provided by the tool
vendors. This instrumented code can monitor and keep an audit of what portions of code are covered. The
tools also allow reporting on the portions of the code that are covered frequently, so that the critical or
most-often (login )portions of code can be identified.

Code coverage testing is made up of the following types of coverage.

1. Statement coverage
2. Path coverage
3. Condition coverage
4. Function coverage

[Link] Statement coverage

Program constructs in most conventional programming languages can be classified as

1. Sequential control flow


2. Two-way decision statements like if then else
3. Multi-way decision statements like Switch
4. Loops like while do, repeat until and for

Object-oriented languages have all of the above and, in addition, a number of other constructs and
concepts. We will take up issues pertaining to object oriented languages together in Chapter 11. We will
confine our discussions here to conventional languages.

Statement coverage refers to writing test cases that execute each of the program statements. One can start
with the assumption that more the code covered, the better is the testing of the functionality, as the code
27
realizes the functionality. Based on this assumption, code coverage can be achieved by providing
coverage to each of the above types of statements.

Loop constructs present more variations to take care of. A loop—in various forms such as for, while,
repeat, and so on—is characterized by executing a set of statements repeatedly until or while certain
conditions are met. A good percentage of the defects in programs come about because of loops that do not
function properly. More often, loops fail in what are called “boundary conditions.” One of the
common looping errors is that the termination condition of the loop is not properly stated. In order
to make sure that there is better statement coverage for statements within a loop, there should be
test cases that

1. Skip the loop completely, so that the situation of the termination condition being true
before starting the loop is tested.
2. Exercise the loop between once and the maximum number of times, to check all possible
“normal” operations of the loop.
3. Try covering the loop, around the “boundary” of n—that is, just below n, n, and just
above n.

The statement coverage for a program, which is an indication of the percentage of statements actually
executed in a set of tests, can be calculated by the formula given alongside in the margin.

Statement Coverage=(Total statements exercised / Total number of executable statements in


program) * 100

Eg: 125/200*100= 62.5%

It is clear from the above discussion that as the type of statement progresses from a simple sequential
statement to if then else and through to loops, the number of test cases required to achieve statement
coverage increases. Taking a cue from the Dijkstra's Doctrine in Chapter 1, just as exhaustive testing of
all possible input data on a program is not possible, so also exhaustive coverage of all statements in a
program will also be impossible for all practical purposes.

Even if we were to achieve a very high level of statement coverage, it does not mean that the program is
defect-free. First, consider a hypothetical case when we achieved 100 percent code coverage. If the
program implements wrong requirements and this wrongly implemented code is “fully tested,” with 100
percent code coverage, it still is a wrong program and hence the 100 percent code coverage does not mean
anything.

[Link] Path coverage

In path coverage, we split a program into a number of distinct paths. A program (or a part of a
program) can start from the beginning and take any of the paths to its completion.

1. Node coverage
2. Link coverage
28
3. Loop coverage

Path Coverage = (Total paths exercised / Total number of paths in program) * 100

Path coverage refers : to designing test cases such that all linearly independent paths in the
program are executed at least once .

Let us take an example of a date validation routine. The date is accepted as three fields mm, dd and yyyy.
We have assumed that prior to entering this routine, the values are checked to be numeric. To simplify the
discussion, we have assumed the existence of a function called leapyear which will return TRUE if the
given year is a leap year. There is an array called DayofMonth which contains the number of days in each
month. A simplified flow chart for this is given in Figure 3.2 below.

Figure 3.2 Flow chart for a date validation routine.

As can be seen from the figure, there are different paths that can be taken through the program. Each part
of the path is shown in red. The coloured representation of Figure 3.2 is available on Illustrations. Some
of the paths are

 A
 B-D-G
 B-D-H
 B-C-E-G

29
 B-C-E-H
 B-C-F-G
 B-C-F-H

Regardless of the number of statements in each of these paths, if we can execute these paths, then we
would have covered most of the typical scenarios.

Path coverage provides a stronger condition of coverage than statement coverage as it relates to the
various logical paths in the program rather than just program statements.

[Link] Condition coverage (Boolean expressions ) True /False both to check

In the above example, even if we have covered all the paths possible, it would not mean that the program
is fully tested. For example, we can make the program take the path A by giving a value less than 1 (for
example, 0) to mm and find that we have covered the path A and the program has detected that the month
is invalid. But, the program may still not be correctly testing for the other condition namely mm > 12.
Furthermore, most compliers perform optimizations to minimize the number of Boolean operations and
all the conditions may not get evaluated, even though the right path is chosen. For example, when there is
an OR condition (as in the first IF statement above), once the first part of the IF (for example, mm < 1) is
found to be true, the second part will not be evaluated at all as the overall value of the Boolean is TRUE.
Similarly, when there is an AND condition in a Boolean expression, when the first condition evaluates
to FALSE, the rest of the expression need not be evaluated at all.

For all these reasons, path testing may not be sufficient. It is necessary to have test cases that exercise
each Boolean expression and have test cases test produce the TRUE as well as FALSE paths. Obviously,
this will mean more test cases and the number of test cases will rise exponentially with the number of
conditions and Boolean expressions. However, in reality, the situation may not be very bad as these
conditions usually have some dependencies on one another.

Condition Coverage = (Total decisions exercised / Total number of decisions in program ) * 100

The condition coverage, as defined by the formula alongside in the margin gives an indication of the
percentage of conditions covered by a set of test cases. Condition coverage is a much stronger criteria
than path coverage, which in turn is a much stronger criteria than statement coverage.

[Link] Function coverage

This is a new addition to structural testing to identify how many program functions (similar to functions
in “C” language) are covered by test cases.

The requirements of a product are mapped into functions during the design phase and each of the
functions form a logical unit. For example, in a database software, “inserting a row into the
database” could be a function. Or, in a payroll application, “calculate tax” could be a function. Each
function could, in turn, be implemented using other functions. While providing function coverage, test

30
cases can be written so as to exercise each of the different functions in the code. The advantages that
function coverage provides over the other types of coverage are as follows.

1. Functions are easier to identify in a program and hence it is easier to write test cases to
provide function coverage.
2. Since functions are at a much higher level of abstraction than code, it is easier to achieve
100 percent function coverage than 100 percent coverage in any of the earlier methods.
3. Functions have a more logical mapping to requirements and hence can provide a more
direct correlation to the test coverage of the product.
4. Since functions are a means of realizing requirements, the importance of functions can be
prioritized based on the importance of the requirements they realize. Thus, it would be
easier to prioritize the functions for testing. This is not necessarily the case with the earlier
methods of coverage.
5. Function coverage provides a natural transition to black box testing.

We can also measure how many times a given function is called. This will indicate which functions are
used most often and hence these functions become the target of any performance testing and optimization.
As an example, if in a networking software, we find that the function that assembles and disassembles the
data packets is being used most often, it is appropriate to spend extra effort in improving the quality and
performance of that function. Thus, function coverage can help in improving the performance as well as
quality of the product.

Function Coverage = (Total functions exercised / Total number of functions in program) * 100

[Link] Summary

Code coverage testing involves “dynamic testing” methods of executing the product with pre-written test
cases, and finding out how much of code has been covered. If a better coverage of a code is desired,
several iterations of testing may be required. For each iteration, one has to go through the statistics and
write a new set of test cases for covering portions of the code not covered by earlier test cases. To do this
type of testing not only does one need to understand the code, logic but also need to understand how to
write effective test cases that can cover good portions of the code. This type of testing can also be referred
to as “gray box testing” as this uses the combination of “white box and black box methodologies”
(white + black = gray) for effectiveness.

Thus, better code coverage is the result of better code flow understanding and writing effective test cases.
Code coverage up to 40-50 percent is usually achievable. Code coverage of more than 80 percent requires
enormous amount of effort and understanding of the code.

The multiple code coverage techniques we have discussed so far are not mutually exclusive. They
supplement and augment one another. While statement coverage can provide a basic comfort factor, path,
decision, and function coverage provide more confidence by exercising various logical paths and
functions.

31
In the above discussion, we have looked at the use of code coverage testing for various functional
requirements. There are also a few other uses for these methods.

Uses of Code coverage testing

Performance analysis and optimization Code coverage tests can identify the areas of a code that are
executed most frequently. Extra attention can then be paid to these sections of the code. If further
performance improvement is no longer possible, then other strategies like caching can be considered.
Code coverage testing provides information that is useful in making such performance-oriented decisions.

Resource usage analysis White box testing, especially with instrumented code, is useful in identifying
bottlenecks in resource usage. For example, if a particular resource like the RAM or network is perceived
as a bottleneck, then instrumented code can help identify where the bottlenecks are and point towards
possible solutions.

Checking of critical sections or concurrency related parts of code Critical sections are those parts of
a code that cannot have multiple processes executing at the same time. Coverage tests with instrumented
code is one of the best means of identifying any violations of such concurrency constraints through
critical sections.

Identifying memory leaks Every piece of memory that is acquired or allocated by a process (for
example, by malloc in C) should be explicitly released (for example, by free in C). If not, the acquired
memory is “lost” and the amount of available memory decreases correspondingly. Over time, there would
be no memory available for allocation to meet fresh memory requests and processes start failing for want
of memory. The various white box testing methods can help identify memory leaks. Most debuggers or
instrumented code can tally allocated and freed memory.

Dynamically generated code (web services ) White box testing can help identify security holes
effectively, especially in a dynamically generated code. In instances where a piece of code is dynamically
created and executed, the functionality of the generated code should be tested on the fly. For example,
when using web services, there may be situations wherein certain parameters are accepted from the users
and html/java code may be generated and passed on to a remote machine for execution. Since after the
transaction or service is executed, the generated code ceases to exist, testing the generated code requires
code knowledge. Hence, the various techniques of white box testing discussed in this chapter come in
handy.

3.3.3 Code Complexity TestingIn previous sections, we saw the different types of coverage that can be
provided to test a program. Two questions that come to mind while using these coverage are:

1. Which of the paths are independent? If two paths are not independent, then we may be able to
minimize the number of tests.
2. Is there an upper bound on the number of tests that must be run to ensure that all the statements
have been executed at least once?

Cyclomatic complexity is a metric that quantifies the complexity of a program and thus provides
answers to the above questions.

32
Cyclomatic complexity: is a source code complexity measurement that is being correlated to a number
of coding errors. It is calculated by developing a Control Flow Graph of the code that measures the
number of linearly-independent paths through a program module.

A program is represented in the form of a flow graph. A flow graph consists of nodes and edges. In order
to convert a standard flow chart into a flow graph to compute cyclomatic complexity, the following steps
can be taken.

1. Identify the predicates or decision points (typically the Boolean conditions or conditional
statements) in the program.
2. Ensure that the predicates are simple (that is, no and/or, and so on in each predicate).
Similarly, if there are loop constructs, break the loop termination checks into simple
predicates.
3. Combine all sequential statements into a single node. The reasoning here is that these
statements all get executed, once started.
4. When a set of sequential statements are followed by a simple predicate (as simplified in (2)
above), combine all the sequential statements and the predicate check into one node and have
two edges emanating from this one node. Such nodes with two edges emanating from them are
called predicate nodes.
5. Make sure that all the edges terminate at some node; add a node to represent all the sets of
sequential statements at the end of the program.

Figure 3.3 Flow graph translation of an OR to a simple predicate.

33
We have illustrated the above transformation rules of a conventional flow chart to a flow diagram
in Figure 3.4. We have color coded the different boxes (coloured figure on Illustrations) so that the reader
can see the transformations more clearly. The flow chart elements of a given color on the left-hand side
get mapped to flow graph elements of the corresponding nodes on the right-hand side.

Flow Graph Notation

Notation for representing control flow

 Arrows called edges represent flow of control


 Circles called nodes represent one or more actions.

 Areas bounded by edges and nodes called regions.

 A predicate node is a node containing a condition

34
Figure 3.4 Converting a conventional flow chart to a flow graph.

Cyclomatic Complexity = Number of Predicate Nodes + 1

Cyclomatic Complexity = E – N + 2

E= edges, N=nodes

Intuitively, a flow graph and the cyclomatic complexity provide indicators to the complexity of the logic
flow in a program and to the number of independent paths in a program. The primary contributors to both
the complexity and independent paths are the decision points in the program. Consider a hypothetical
program with no decision points. The flow graph of such a program (shown in Figure 3.5 above) would
have two nodes, one for the code and one for the termination node. Since all the sequential steps are
combined into one node (the first node), there is only one edge, which connects the two nodes. This edge
is the only independent path. Hence, for this flow graph, cyclomatic complexity is equal to one.

35
Figure 3.5 A hypothetical program with no decision node.

This graph has no predicate nodes because there are no decision points. Hence, the cyclomatic complexity
is also equal to the number of predicate nodes (0) + 1.

Note that in this flow graph, the edges (E) = 1; nodes (N) = 2. The cyclomatic complexity is also equal
to 1 = 1 + 2 – 2 = E – N + 2.

(1-2+2)=-1+2=1

When a predicate node is added to the flow graph (shown in Figure 3.6 above), there are obviously two
independent paths, one following the path when the Boolean condition is TRUE and one when the
Boolean condition is FALSE. Thus, the cyclomatic complexity of the graph is 2.

36
Figure 3.6 Adding one decision node.

Incidentally, this number of independent paths, 2, is again equal to the number of predicate nodes (1) + 1.
When we add a predicate node (a node with two edges), complexity increases by 1, since the “E” in the E
– N + 2 formula is increased by one while the “N” is unchanged. As a result, the complexity using the
formula E – N + 2 also works out to 2.

From the above reasoning, the reader would hopefully have got an idea about the two different ways to
calculate cyclomatic complexity and the relevance of cyclomatic complexity in identifying independent
paths through a program. We have summarized these formulae below. We are not going to formally prove
these formulae. Suffice to say that these formulae are extremely useful.

The above two formulae provide an easy means to calculate cyclomatic complexity, given a flow graph.
In fact the first formula can be used even without drawing the flow graph, by simply counting the number
of the basic predicates. There are other formulations of cyclomatic complexity derived from the
foundations of Graph Theory, which we have not covered here. The references given at the end can
provide pointers for the interested reader.

Using the flow graph, an independent path can be defined as a path in the flow graph that has at least one
edge that has not been traversed before in other paths. A set of independent paths that cover all the edges
is a basis set. Once the basis set is formed, test cases should be written to execute all the paths in the basis
set.

[Link] Calculating and using cyclomatic complexity

For small programs cyclomatic complexity can be calculated manually, but automated tools are essential
as several thousands of lines of code are possible in each program in a project. It will be very difficult to
manually create flow graphs for large programs. There are several tools that are available in the market
which can compute cyclomatic complexity. But, we would like to caution that calculating the complexity
of a module after it has been built and tested may be too late—it may not be possible to redesign a
complex module after it has been tested. Thus some basic complexity checks must be performed on the
modules before embarking upon the testing (or even coding) phase. This can become one of the items to
check for in a code review. Based on the complexity number that emerges from using the tool, one can
conclude what actions need to be taken for complexity measure using Table 3.1.

Table 3.1

Complexity What it means

1–10 Well-written code, testability is high, cost/effort to maintain is low

37
Complexity What it means

10–20 Moderately complex, testability is medium, cost/effort to maintain is medium

20–40 Very complex, testability is low, cost/effort to maintain is high

>40 Not testable, any amount of money/effort to maintain may not be enough

3.4 CHALLENGES IN WHITE BOX TESTING

White box testing requires a sound knowledge of the program code and the programming language. This
means that the developers should get intimately involved in white box testing. Developers, in general, do
not like to perform testing functions. This applies to structural testing as well as static testing methods
such as reviews. In addition, because of the timeline pressures, the programmers may not “find time” for
reviews (an euphemism for wanting to do more coding).

Human tendency of a developer being unable to find the defects in his or her code As we saw
earlier, most of us have blind spots in detecting errors in our own products. Since white box testing
involves programmers who write the code, it is quite possible that they may not be most effective in
detecting defects in their own work products. An independent perspective could certainly help.

Fully tested code may not correspond to realistic scenarios Programmers generally do not have a full
appreciation of the external (customer) perspective or the domain knowledge to visualize how a product
will be deployed in realistic scenarios. This may mean that even after extensive testing, some of the
common user scenarios may get left out and defects may creep in.

These challenges do not mean that white box testing is ineffective. But when white box testing is carried
out and these challenges are addressed by other means of testing, there is a higher likelihood of more
effective testing. Black box testing, to be discussed in the following chapter addresses some of these
challenges.

38

You might also like