Module-5 ST
Module-5 ST
MODULE -5
Syllabus
effective in exercising the full range of module behaviors, rather than just those that are easy to trigger
and observe in a particular context of other modules. While integration testing may to some extent
act as a process check on module testing (i.e., faults revealed during integration test can be taken as
a signal of unsatisfactory unit testing), thorough integration testing cannot fully compensate for
sloppiness at the module level.
Integration Faults
inctx->ssl = NULL;
inctx->filter ctx->pssl = NULL;
}
Working from the top level (in terms of “use”or “include” relation) toward the bottom.
No drivers required if program tested from top-level interface (e.g. GUI, CLI, web app, etc.)
Top
Component contract or interface The component contract describes the access points and
parameters of the component, and specifies functional and nonfunctional behavior and any conditions
required for using the component.
Frameworks and design patterns Patterns are logical design fragments, while frameworks are
concrete elements of the application. Frameworks often implement patterns.
Component-based system A component-based system is a system built primarily by assembling
software components (and perhaps a small amount of application specific code) connected through a
framework or ad hoc "glue code."
COTS The term commercial off-the-shelf, or COTS, indicates components developed for the sale to
other organizations.
System testing is a check of consistency between the software system and its specification (it is a
verification activity). Like unit and integration testing, system testing is primarily aimed at
uncovering faults, but unlike testing activities at finer granularity levels, system testing focuses on
system-level properties. System testing together with acceptance testing also serves an important role
in assessing whether a product can be released to customers, which is distinct from its role in exposing
faults to be removed to improve the product.
System Testing
The essential characteristics of system testing are that it is comprehensive, based on a specification
of observable behavior, and independent of design and implementation decisions. System testing can
be considered the culmination of integration testing, and passing all system tests is tantamount to
being complete and free of known bugs. The system test suite may share some test cases with test
suites used in integration and even unit testing, particularly when a thread-based or spiral model of
development has been taken and subsystem correctness has been tested primarily through externally
visible features and behavior. However, the essential characteristic of independence implies that test
cases developed in close coordination with design and implementation may be unsuitable. The
overlap, if any, should result from using system test cases early, rather than reusing unit and
integration test cases in the system test suite. Independence in system testing avoids repeating
software design errors in test design. This danger exists to some extent at all stages of development,
but always in trade for some advantage in designing effective test cases based on familiarity with the
software design and its potential pitfalls. The balance between these considerations shifts at different
levels of granularity, and it is essential that independence take priority at some level to obtain a
credible assessment of quality.
Acceptance Testing
The purpose of acceptance testing is to guide a decision as to whether the product in its current
state should be released. The decision can be based on measures of the product or process. Measures
of the product are typically some inference of dependability based on statistical testing. Measures of
the process are ultimately based on comparison to experience with previous products.
Although system and acceptance testing are closely tied in many organizations, fundamental
differences exist between searching for faults and measuring quality. Even when the two activities
overlap to some extent, it is essential to be clear about the distinction, in order to avoid drawing
unjustified conclusions.
Quantitative goals for dependability, including reliability, availability, and mean time
between failures. These are essentially statistical measures and depend on a statistically valid
approach to drawing a representative sample of test executions from a population of program
behaviors. Systematic testing, which includes all of the testing techniques presented heretofore in this
book, does not draw statistically representative samples. Their purpose is not to fail at a "typical"
rate, but to exhibit as many failures as possible. They are thus unsuitable for statistical testing.
Usability
A usable product is quickly learned, allows users to work efficiently, and is pleasant to use. Usability
involves objective criteria such as the time and number of operations required to perform tasks and
the frequency of user error, in addition to the overall, subjective satisfaction of users.
Even if usability is largely based on user perception and thus is validated based on user feedback, it
can be verified early in the design and through the whole software life cycle. The process of verifying
and validating usability includes the following main steps:
Inspecting specifications with usability checklists. Inspection provides early feedback on usability.
Testing early prototypes with end users to explore their mental model (exploratory test), evaluate
alternatives (comparison test), and validate software usability. A prototype for early assessment of
usability may not include any functioning software; a cardboard prototype may be as simple as a
sequence of static images presented to users by the usability tester.
Testing incremental releases with both usability experts and end users to monitor progress and
anticipate usability problems.
System and acceptance testing that includes expert-based inspection and testing, user based testing,
comparison testing against competitors, and analysis and checks often done automatically, such as a
check of link connectivity and verification of browser compatibility.
Regression Testing
When building a new version of a system (e.g., by removing faults, changing or adding
functionality, porting the system to a new platform, or extending interoperability), we may also
change existing functionality in unintended ways. Sometimes even small changes can produce
unforeseen effects that lead to new failures. For example, a guard added to an array to fix an overflow
problem may cause a failure when the array is used in other contexts, or porting the software to a new
platform may expose a latent fault in creating and modifying temporary files.
When a new version of software no longer correctly provides functionality that should be
preserved, we say that the new version regresses with respect to former versions. The nonregression
of new versions (i.e., preservation of functionality), is a basic quality requirement. Disciplined design
and development techniques, including precise specification and modularity that encapsulates
independent design decisions, improves the likelihood of achieving nonregression. Testing activities
that focus on regression problems are called (non) regression testing. Usually "non" is omitted and
we commonly say regression testing.
A simple approach to regression testing consists of reexecuting all test cases designed for
previous versions.
Even this simple retest all approach may present nontrivial problems and costs.
Former test cases may not be reexecutable on the new version without modification, and
rerunning all test cases may be too expensive and unnecessary.
A good quality test suite must be maintained across system versions.
Even when we can identify and eliminate obsolete test cases, the number of tests to be reexecuted
may be large, especially for legacy software. Executing all test cases for large software products may
require many hours or days of execution and may depend on scarce resources such as an expensive
hardware test harness.
For example, some mass market software systems must be tested for compatibility with hundreds of
different hardware configurations and thousands of drivers. Many test cases may have been designed
to exercise parts of the software that cannot be affected by the changes in the version under test. Test
cases designed to check the behavior of the file management system of an operating system is unlikely
to provide useful information when reexecuted after changes of the window manager. The cost of
reexecuting a test suite can be reduced by selecting a subset of test cases to be reexecuted, omitting
irrelevant test cases or prioritizing execution of subsets of the test suite by their relation to changes.
Regression test selection techniques are based on either code or specifications. Code based
selection techniques select a test case for execution if it exercises a portion of the code that has been
modified. Specification-based criteria select a test case for execution if it is relevant to a portion of
the specification that has been changed. Code based regression test techniques can be supported by
relatively simple tools. They work even when specifications are not properly maintained. However,
like code-based test techniques in general, they do not scale well from unit testing to integration and
system testing. In contrast, specification-based criteria scale well and are easier to apply to changes
that cut across several modules. However, they are more challenging to automate and require
carefully structured and well-maintained specifications.
Regression testing criteria may select a large portion of a test suite. When a regression test suite is
too large, we must further reduce the set of test cases to be executed.
Random sampling is a simple way to reduce the size of the regression test suite. Better
approaches prioritize test cases to reflect their predicted usefulness. In a continuous cycle of retesting
as the product evolves, high-priority test cases are selected more often than low-priority test cases.
With a good selection strategy, all test cases are executed sooner or later, but the varying periods
result in an efficient rotation in which the cases most likely to reveal faults are executed most
frequently.
Priorities can be assigned in many ways. A simple priority scheme assigns priority according
to the execution history: Recently executed test cases are given low priority, while test cases that have
not been recently executed are given high priority. In the extreme, heavily weighting execution
history approximates round robin selection.
Other history-based priority schemes predict fault detection effectiveness. Test cases that have
revealed faults in recent versions are given high priority. Faults are not evenly distributed, but tend
to accumulate in particular parts of the code or around particular functionality. Test cases that
exercised faulty parts of the program in the past often exercise faulty portions of subsequent revisions.
Structural coverage leads to a set of priority schemes based on the elements covered by a test case.
We can give high priority to test cases that exercise elements that have not recently been exercised.
Both the number of elements covered and the "age" of each element (time since that element was
covered by a test case) can contribute to the prioritization.
Fig.
Two observations: a clear presumption of functional testing is used here, and an implied bottom–up
testing order is used. Here, “bottom–up” refers to levels of abstraction—unit first, then integration,
and finally, system testing. Of the three main levels of testing (unit, integration, and system), unit
testing is best understood. System testing is understood better than integration testing, but both need
clarification. The bottom–up approach sheds some insight: test the individual components, and then
integrate these into subsystems until the entire system is tested. System testing should be something
that the
Waterfall Spin-Offs
There are three mainline derivatives of the waterfall model: incremental development, evolutionary
development, and the spiral model (Boehm, 1988). Each of these involves a series of increments or
builds as shown in Figure 11.3. It is important to keep preliminary design as an integral phase rather
than to try to amortize such high-level design across a series of builds. (To do so usually results in
unfortunate consequences of design choices made during the early builds that are regrettable in later
builds.) This single design step cannot be done in the evolutionary and spiral models. This is also a
major limitation of the bottom–up agile methods.
Within a build, the normal waterfall phases from detailed design through testing occur with one
important difference: system testing is split into two steps—regression and progression testing. The
main impact of the series of builds is that regression testing becomes necessary. The goal of
regression testing is to ensure that things that worked correctly in the previous build still work with
the newly added code. Regression testing can either precede or follow integration testing, or possibly
occur in both places. Progression testing assumes that regression testing was successful and that the
new functionality can be tested. Regression testing is an absolute necessity in a series of builds
because of the well-known ripple effect of changes to an existing system.
When systems are not fully understood (by either the customer or the developer), functional
decomposition is perilous at best. Barry Boehm jokes when he describes the customer who says “I
don’t know what I want, but I’ll recognize it when I see it.” The rapid prototyping life cycle (Figure
11.4) deals with this by providing the “look and feel” of a system. Thus, in a sense, customers can
recognize what they “see.” In turn, this drastically reduces the specification-to-customer feedback
loop by producing very early synthesis. Rather than build a final system, a “quick and dirty” prototype
is built and then used to elicit customer feedback. Depending on the feedback, more prototyping
cycles may occur. Once the developer and the customer agree that a prototype represents the desired
system, the developer goes ahead and builds to a correct specification. At this point, any of the
waterfall spin-offs might also be used. The agile life cycles are the extreme of this pattern.
Rapid prototyping has no new implications for integration testing; however, it has very
interesting implications for system testing. Where are the requirements? Is the last prototype the
specification?
How are system test cases traced back to the prototype? One good answer to questions such as these
is to use the prototyping cycles as information-gathering activities and then produce a requirements
specification in a more traditional manner. Another possibility is to capture what the customer does
with the prototypes, define these as scenarios that are important to the customer, and then use these
as system test cases. These could be precursors to the user stories of the agile life cycles. The main
contribution of rapid prototyping is that it brings the operational (or behavioral) viewpoint to the
requirements specification phase. Usually, requirements specification techniques emphasize the
structure of a system, not its behavior. This is unfortunate because most customers do not care about
the structure, and they do care about the behavior.
Fig. Rapid prototyping life cycle.
The SATM terminal is sketched in below Figure 4.7 in addition to the display screen, there are
function buttons B1, B2, and B3, a digit keypad with a cancel key, slots for printer receipts and ATM
cards, and doors for deposits and cash withdrawals.
The SATM system is described here with a traditional, structured analysis approach in Figure.
The structured analysis approach to requirements specification is the most widely used method in the
world. It enjoys extensive CASE tool support as well as commercial training. The technique is based
on three complementary models: function, data, and control. Here we use data flow diagrams for the
functional models, entity/relationship models for data, and finite state machine models for the control
aspect of the SATM system.
The functional and data models were drawn with the Deft CASE tool from Sybase Inc. That tool
identifies external devices with lower case letters, and elements of the functional decomposition with
numbers. The open and filled arrowheads on flow arrows signify whether the flow item is simple or
compound. The portions of the SATM system shown here pertain generally to the personal
identification number (PIN) verification portion of the system. The Deft CASE tool distinguishes
between simple and compound flows, where compound flows may be decomposed into other flows,
which may themselves be compound. The graphic appearance of this choice is that simple flows have
filled arrowheads, while compound flows have open arrowheads. As an example, the compound flow
“screen” has the following decomposition: screen is comprised of
screen1 welcome
screen2 enter PIN
screen3 wrong PIN
screen4 PIN failed, card retained
screen5 select trans type
screen6 select account type
screen7 enter amount
screen8 insufficient funds
screen9 cannot dispense that amount
screen10 cannot process withdrawals
screen11 take your cash
screen12 cannot process deposits
screen13 put dep envelop in slot
screen14 another transaction?
screen15 Thanks; take card and receipt
Figure 4.10 is an Entity/Relationship diagram of the major data structures in the SATM system:
Customers, Accounts, Terminals, and Transactions. Good data modeling practice dictates postulating
an entity for each portion of the system that is described by data that is retained (and used by
functional components).
Among the data the system would need for each customer are the customer’s identification and
personal account number (PAN); these are encoded into the magnetic strip on the customer’s ATM
card. We would also want to know information about a customer’s account(s), including the account
numbers, the balances, the type of account (savings or checking), and the Personal Identification
Number (PIN) of the account. At this point, we might ask why the PIN is not associated with the
customer, and the PAN with an account. Some design has crept into the specification at this point: if
the data were as questioned, a person’s ATM card could be used by anyone; as it is, the present
separation predisposes a security checking procedure. Part of the E/R model describes relationships
among the entities: a customer HAS account(s), a customer conducts transaction(s) in a SESSION,
and, independent of customer information, transaction(s) OCCUR at an ATM terminal. The single
and double arrowheads signify the singularity or plurality of these relationships: one customer may
have several accounts and may conduct none or several transactions. Many transactions may occur at
a terminal, but one transaction never occurs at a multiplicity of terminals.
The dataflow diagrams and the entity/relationship model contain information that is
primarily structural. This is problematic for testers, because test cases are concerned with behavior,
not with structure. As a supplement, the functional and data information are linked by a control model;
here we use a finite state machine. The upper level finite state machine in Figure divides the system
into states that correspond to stages of customer usage.
The decomposition of the Await PIN state is shown in Figure 4.12. In both of these figures, state
transitions are caused either by events at the ATM terminal or by data conditions (such as the
recognition that a PIN is correct).
The function, data, and control models are the basis for design activities in the waterfall model (and
its spin-offs). During design, some of the original decisions may be revised based on additional
insights and more detailed requirements. The end result is a functional decomposition such as the
partial one shown in the structure chart in Figure 4.13. Notice that the original first level
decomposition into four subsystems is continued: the functionality has been decomposed to lower
levels of detail.
SATM System Device Sense &Control Door Sense &Control Get DoorStatus ControlDoor
DispenseCash Slot Sense &Control WatchCardSlot Get Deposit Slot Status Control CardRoller
Control Envelope Roller Read Card Strip Central BankComm. Get PIN forPAN Get AccountStatus
Post DailyTransactions Terminal Sense &Control ScreenDriver Key Sensor ManageSession
ValidateCard ValidatePIN Get PIN CloseSession NewTransaction Request PrintReceipt Post
Transaction Local Manage Transaction Get TransactionType Get AccountType ReportBalance
ProcessDeposit ProcessWithdrawal
partial functional decomposition (Figure 4.13). We also developed a PDL description of the main
program and two units, ValidatePIN and GetPIN. We begin here by expanding the functional
decomposition that was started in Figure 4.13; the numbering scheme preserves the levels of the
components in that figure. For easier reference, each component that appears in our analysis is given
a new (shorter) number; these numbers are given in Table 1. (The only reason for this is to make the
figures and spreadsheet more readable.)
Table 1 SATM Units and Abbreviated Names
Unit Number Unit Name
1 SATM System
A Device Sense & Control
D Door Sense & Control
2 Get Door Status
3 Control Door
4 Dispense Cash
E Slot Sense & Control
5 WatchCardSlot
6 Get Deposit Slot Status
7 Control Card Roller
8 Control Envelope Roller
9 Read Card Strip
10 Central Bank Comm.
11 Get PIN for PAN
12 Get Account Status
13 Post Daily Transactions
B Terminal Sense & Control
14 Screen Driver
15 Key Sensor C Manage Session
16 Validate Card
17 Validate PIN
18 GetPIN
F Close Session
19 New Transaction Request
20 Print Receipt
21 Post Transaction Local
22 Manage Transaction
23 Get Transaction Type
24 Get Account Type
25 Report Balance
26 Process Deposit
27 Process Withdrawal
Decomposition-Based Integration
Mainline introductory software engineering texts, for example, Pressman (2005) and Schach
(2002), typically present four integration strategies based on the functional decomposition tree of the
procedural software: top–down, bottom–up, sandwich, and the vividly named “big bang.”
We can dispense with the big bang approach most easily: in this view of integration, all the
units are compiled together and tested at once. The drawback to this is that when (not if!) a failure is
observed, few clues are available to help isolate the location(s) of the fault.
The functional decomposition tree is the basis for integration testing because it is the main
representation, usually derived from final source code, which shows the structural relationship of the
system with respect to its units. All these integration orders presume that the units have been
separately tested; thus, the goal of decomposition-based integration is to test the interfaces among
separately tested units. A functional decomposition tree reflects the lexicological inclusion of units,
in terms of the order in which they need to be compiled, to assure the correct referential scope of
variables and unit names.
Test the interfaces and interactions among separately tested units
Three different approaches
1. Based on functional decomposition
2. Based on call graphs
3. Based on paths
Functional Decomposition
Create a functional hierarchy for the software
Problem is broken up into independent task units, or functions
Units can be run either
Sequentially and in a synchronous call-reply manner
Or simultaneously on different processors
Used during planning, analysis and design
Four strategies
Top-down
Bottom-up
Sandwich
Big bang
Bottom-Up integration
Bottom-Up integration strategy
Focuses on testing the units at the lowest levels first
Gradually includes the subsystems that reference/require the previously tested subsystems
Do until all subsystems are included in the testing
Special driver code is needed to do the testing
The driver is a specialized routine that passes test cases to a subsystem
Subsystem is not everything below current root module, but a sub-tree down to the leaf
level
Sandwich Integration
Combines top-down strategy with bottom-up strategy
Less stub and driver development effort
Added difficulty in fault isolation
Doing big-bang testing on sub-trees
Sandwich example is as shown in the figure.
Pair-Wise Integration
The idea behind Pair-Wise integration testing
Eliminate need for developing stubs / drivers
Use actual code instead of stubs/drivers
In order not to deteriorate the process to a big-bang strategy
Restrict a testing session to just a pair of units in the call graph
Results in one integration test session for each edge in the call graph
Pair-wise integration session example
Neighbourhood integration
The neighbourhood of a node in a graph
The set of nodes that are one edge away from the given node
In a directed graph
All the immediate predecessor nodes and all the immediate successor nodes of a given node
Neighborhood Integration Testing
Reduces the number of test sessions
Fault isolation is more difficult
Neighbourhood integration example
Path-Based Integration
Motivation
Combine structural and behavioral type of testing for integration testing as we did for unit
testing
Basic idea
Focus on interactions among system units
Rather than merely to test interfaces among separately developed and tested units
Interface-based testing is structural while interaction-based is behavioral
Source node
A program statement fragment at which program execution begins or resumes.
For example the first “begin” statement in a program.
MM-Path
An interleaved sequence of module execution paths and messages.
Describes sequences of module execution paths that include transfers of control among
separate units.
MM-paths always represent feasible execution paths, and these paths cross unit boundaries.
There is no correspondence between MM paths and DD paths.
The intersection of a module execution path with a unit is the analog of a slice with respect to
the MM-path function.
MM-Path Example
MM-path Graph
Given a set of units their MM-path graph is the directed graph in which
Nodes are module execution paths
Edges correspond to messages and returns from one unit to another
The definition is with respect to a set of units
It directly supports composition of units and composition based integration testing
MM-path guidelines
How long, or deep, is an MM-path? What determines the end points?
Message quiescence
Occurs when a unit that sends no messages is reached
Module C in the example
Data quiescence
Occurs when a sequence of processing ends in the creation of stored data that is not
immediately used (path D1 and D2)
MM-Path metric
How many MM-paths are sufficient to test a system
Should cover all source-to-sink paths in the set of units
What about loops?
Use condensation graphs to get directed acyclic graphs
Avoids an excessive number of paths