Unit- 1 STM Notes
Unit- 1 STM Notes
UNIT – I
Introduction:
Purpose of testing
Dichotomies
model for testing
consequences of bugs
taxonomy of bugs
Predicates
path predicates and
achievable paths
path sensitizing
path instrumentation,
application of path testing
Introduction:
What is software testing?
Software testing is a method to assess the functionality of the software program. The process checks
whether the actual software matches the expected requirements and ensures the software is bug-free.
It mainly aims at measuring the specification, functionality, and performance of a software program or
application.
Detect Defects early: the primary goal of testing is to identify bugs and defects before they
impact users.
Maintaining: Preserving stability through regression testing and safeguarding critical areas like
usability, compatibility, and security.
Enhancing: Improving reliability, optimizing performance, ensuring functionality, and aligning
the product with user needs.
Build Trust and Satisfaction: consistent testing creates a stable and dependable product that
meets user expectations, fostering trust and loyalty.
Identify Vulnerabilities to Mitigate Risks: in high-stakes industries like finance, healthcare, and
law, testing prevents costly errors that could harm users or expose companies to legal risks.
Types of software testing
There are two major software testing types that Quality Assurance professionals frequently use,
including:
Functional testing: a type of software testing to verify whether the application delivers the
expected output.
Non-functional testing: a type of software testing to verify whether the non-functional aspects
of an application (e.g., stability, security, and usability) are working as expected.
Software Testing Life Cycle: consists of 6 key activities to ensure that all software quality goals
are met, as shown below:
“History says that even well written programs still have 1-3 bugs per hundred statements.”
Phase 0: (Until 1956: Debugging Oriented): There is no difference between testing and
debugging. Phase 0 thinking was the norm in early days of software development till testing
emerged as a discipline.
Phase 1: (1957-1978: Demonstration Oriented):
The purpose of testing here is to show that software works.
Highlighted during the late 1970s.
This failed because the probability of showing that software works 'decreases' as testing
increases. i.e. The more you test, the more likely you’ll find a bug.
Phase 2: (1979-1982: Destruction Oriented)
The purpose of testing is to show that software doesn’t work.
This also failed because the software will never get released as you will find one bug or the
other.
Also, a bug corrected may also lead to another bug.
Phase 3: (1983-1987: Evaluation Oriented)
The purpose of testing is not to prove anything but to reduce the perceived risk of not
working to an acceptable value (Statistical Quality Control).
Notion is that testing does improve the product to the extent that testing catches bugs and
to the extent that those bugs are fixed.
The product is released when the confidence on that product is high enough
Phase 4: (1988-2000: Prevention Oriented)
Testability is the factor considered here.
One reason is to reduce the labor of testing. Other reason is to check the testable and non-
testable code.
Testable code has fewer bugs than the code that's hard to test.
Identifying the testing techniques to test the code is the main key here.
1.3 Testing isn't everything: There are approaches other than testing to create better software.
Methods other than testing include:
Inspection Methods: Walkthroughs, desk checking, inspections, and code reading are as
effective as testing but catch different bugs.
Design Style: When designing software, following principles like testability, openness, and
clarity can help to prevent bugs.
Static Analysis Methods: Includes formal analysis of source code during compilation.
Languages: Programmers find new bugs while using new languages.
Development Methodologies and Development Environment: The way software is
developed and the environment it’s used in can help prevent many bugs.
Pesticide Paradox: Every method used to prevent or find bugs still leaves behind some subtle
bugs that it can't catch.
Complexity Barrier : software complexity grows to the limits of our ability to manage that
complexity
2. Dichotomies
The separation that exists between two groups or things that are completely opposite to and
different from each other
Purpose of testing is to show that a program has bugs. The purpose of testing is to find the
error or misconception that led to the program's failure and to design and implement the
program changes that correct the error.
Debugging usually follows testing, but they differ as to goals, methods and most important
psychology.
Testing Debugging
Starts with known conditions. User predefined Starts with possibly unknown initial conditions &
procedure & Has predictable outcomes. End cannot be predicted.
Planned, Designed and Scheduled. Procedures & Duration are not constrained.
Should be predictable, dull, constrained, rigid & There are intuitive leaps, conjectures,
in human. experimentation & freedom.
Test designer is the person who designs the tests where as the tester is the one actually
tests the code.
During functional testing, the designer and tester are probably different persons. During unit
testing, the tester and the programmer merge into one person.
Tests designed and executed by the software designers are by nature biased towards
structural consideration and therefore suffer the limitations of structural testing.
Modularity Efficiency
Easier to design large modules & smaller Less complex & efficient.
interfaces at a higher level.
Small Big
Simple, easier to understand, with fewer Highly complex, with multiple modules,
dependencies. dependencies, and interactions
Easier to test, with fewer test cases and Require extensive testing strategies,
simpler debugging. automation, and continuous integration
Most software is written and used by the same organization. Unfortunately, this situation is
dishonest because it clouds accountability.
If there is no separation between builder and buyer, there can be no accountability.
3.1 ENVIRONMENT:
A Program's environment is the hardware and software required to make it run. For online
systems, the environment may include communication lines, other systems, terminals and
operators.
The environment also includes all programs that interact with and are used to create the
program under test - such as OS, linkage editor, loader, compiler, utility routines.
Because the hardware and firmware are stable, it is not smart to blame the environment for
bugs.
The World The Model World
Environment
Environment Unexpected
Model
Expected
Program
Program Tests Outcome
Model
Nature &
Bug Model
Psychology
3.2 PROGRAM:
3.3 BUGS:
Bugs are more insidious (deceiving but harmful) than ever we expect them to be.
An unexpected test result may lead us to change our notion of what a bug is and our model
of bugs.
Some optimistic notions that many programmers or testers have about bugs are usually
unable to test effectively and unable to justify the dirty tests most programs need.
Benign Bug Hypothesis: The belief that bugs are nice, tame and logical. (Benign: Not
Dangerous)
Bug Locality Hypothesis: The belief that a bug discovered with in a component effects only
that component's behavior.
Control Bug Dominance: The belief those errors in the control structures (if, switch etc) of
programs dominate the bugs.
Code / Data Separation: The belief that bugs respect the separation of code and data.
Lingua Salvator Est: The belief that the language syntax and semantics (e.g. Structured
Coding, Strong typing, etc) eliminates most bugs.
Corrections Abide: The mistaken belief that a corrected bug remains corrected.
Silver Bullets: The mistaken belief that X (Language, Design method, representation,
environment) grants immunity from bugs.
Sadism Suffices: The common belief (especially by independent tester) that a sadistic
streak, low cunning, and intuition are sufficient to eliminate most bugs. Tough bugs need
methodology and techniques.
Angelic Testers: The belief that testers are better at test design than programmers are at
code design
3.4 TESTS:
Formal procedures
Input Preparation
Outcome prediction and Observation
Documentation of test
Execution and observation of outcome are subject to errors
An unexpected test result may lead us to revise the test and test model
Unit/Component testing
Integration testing
System testing
Unit Testing:
Component Testing:
It is an integrated aggregate of one or more units
It verifies the component against functional specifications and the implemented
structure against the design
Component testing is performed before Integration testing and after unit testing.
QA team will do the component testing.
Problems revealed are component bugs.
System Testing:
Testing is like playing a pool game. Either you hit the ball to any pocket (kiddie pool) or you
specify the pocket in advance (real pool). So is the testing.
There is a kiddie testing and real testing. In kiddies testing, the observed outcome will be
considered as the expected outcome.
In real testing, the outcome is predicted and documented before the test is run.
The tester who cannot make that kind of predictions does not understand the program's
functional objectives.
Oracles: An oracle is any program, process, or body of data that specifies the expected outcome
of a set of tests as applied to a tested object.
Example of oracle: Input/Outcome Oracle - an oracle that specifies the expected outcome for
a specified input.
Sources of Oracles:. The hardest part of test design is predicting the expected outcome, but we
often have oracles that reduce the work. They are:
Kiddie Testing
Regression Test Suites
Purchased Suits and Oracles
Existing Program
If the objective of the testing were to prove that a program is free of bugs, then testing
not only would be practically impossible, but also would be theoretically impossible.
Three different approaches can be used to demonstrate that a program is correct. They are:
Functional Testing:
1. Every program operates on a finite number of inputs. A complete functional test would
consists of subjecting the program to all possible input streams.
2. For each input the routine either accepts the stream and produces a correct outcome, accepts
the stream and produces an incorrect outcome, or rejects the stream and tells us that it did so.
Structural Testing:
The design should have enough tests to ensure that every path through the routine is
exercised at least once. Right off that’s impossible because some loops might never terminate.
The number of paths through a small routine can be awesome because each loop multiplies
the path count by the number of times through the loop.
4.1 Importance's of Bugs: IMPORTANCE OF BUGS: The importance of bugs depends on frequency,
correction cost, installation cost, and consequences.
1. Frequency: How often does that kind of bug occur? Pay more attention to the more frequent
bug types.
2. Correction Cost: What does it cost to correct the bug after it is found? The cost is the sum of 2
factors: (1) the cost of discovery (2) the cost of correction. These costs go up dramatically later
in the development cycle when the bug is discovered. Correction cost also depends on system
size.
3. Installation Cost: Installation cost depends on the number of installations: small for a single
user program but more for distributed systems. Fixing one bug and distributing the fix could
exceed the entire system's development cost.
4. Consequences: What are the consequences of the bug? Bug consequences can range from
mild to catastrophic.
The consequences of a bug can be measure in terms of human rather than machine. Some
consequences of a bug on a scale of one to ten are:
1. Mild: The symptoms of the bug offend us aesthetically (gently); a misspelled output or a
misaligned printout.
2. Moderate: Outputs are misleading or redundant. The bug impacts the system's performance.
3. Annoying: The system's behavior because of the bug is dehumanizing. E.g. Names are
truncated or arbitrarily modified.
4. Disturbing: It refuses to handle legitimate (authorized / legal) transactions. The ATM wont give
you money. My credit card is declared invalid.
5. Serious: It loses track of its transactions. Not just the transaction itself but the fact that the
transaction occurred. Accountability is lost.
6. Very Serious: The bug causes the system to do the wrong transactions. Instead of losing your
paycheck, the system credits it to another account or converts deposits to withdrawals.
7. Extreme: The problems aren't limited to a few users or too few transaction types. They are
frequent and arbitrary instead of sporadic infrequent) or for unusual cases.
8. Intolerable: Long term unrecoverable corruption of the database occurs and the corruption is
not easily discovered. Serious consideration is given to shutting the system down.
9. Catastrophic: The decision to shut down is taken out of our hands because the system fails.
10. Infectious: What can be worse than a failed system? One that corrupt other systems even
though it doesn’t fall in itself ; that erodes the social physical environment; that melts nuclear
reactors and starts war.
Correction Cost: Not so important because catastrophic bugs may be corrected easier and
small bugs may take major time to debug.
Context and Application Dependency: Severity depends on the context and the application in
which it is used.
Creating Culture Dependency: What's important depends on the creators of software and
their cultural aspirations. Test tool vendors are more sensitive about bugs in their software
then games software vendors.
User Culture Dependency: Severity also depends on user culture. Naive users of PC software
go crazy over bugs where as pros (experts) may just ignore.
The software development phase: Severity depends on development phase. Any bugs gets
more severe as it gets closer to field use and more severe the longer it has been around.
List all nightmares in terms of the symptoms & reactions of the user to their consequences.
Convert the consequences of into a cost. There could be rework cost. (but if the scope extends
to the public, there could be the cost of lawsuits, lost business, nuclear reactor meltdowns.)
Order these from the costliest to the cheapest. Discard those with which you can live with.
Based on experience, measured data, intuition, and published statistics postulate the kind of
bugs causing each symptom. This is called “bug design processes.” A bug type can cause
multiple symptoms.
Order the causative bugs by decreasing probability (judged by intuition, experience, statistics
etc.).
Calculate the importance of a bug type as:
5. Taxonomy of Bugs
Additional information
Examples
“Real-World Case Study: Feature Interaction Bug in Boeing 737 MAX (2018-2019)”
2. Structural Bugs
• Structural bugs are defects related to the internal design, architecture, and implementation of
a software system.
• These issues affect the maintainability, scalability, and overall integrity of the system.
Control and sequence bugs include paths left out, unreachable code, improper nesting of loops,
loop-back or loop termination criteria incorrect, missing process steps, duplicated processing,
unnecessary processing, rampaging, GOTO's, ill-conceived (not properly planned) switches,
sphagetti code, and worst of all, pachinko code.
This area can be analyzed using theories, which makes it more likely to have systematic errors.
Many control flow bugs are easily tested and caught in unit testing.
Old codebases (especially in Assembly Language Programming (ALP) and COBOL) are heavily
dominated by control flow bugs.
b) Logic Bugs
C) Processing Bugs:
d) Initialization Bugs:
Initialization bugs are common. Initialization bugs can be improper and superfluous.
Superfluous bugs are generally less harmful but can affect performance.
Typical initialization bugs include: Forgetting to initialize the variables before first use,
assuming that they are initialized elsewhere, initializing to the wrong format, representation or
type etc
Explicit declaration of all variables, as in Pascal, can reduce some initialization problems.
3 ) Data bugs : Depend on the types of data or the representation of data. There are 4 sub
categories.
a) Generic Data Bugs: These bugs occur due to incorrect handling of data in general, regardless of
its type or state.
Examples: Data corruption, Data truncation, Incorrect data type handling, Invalid or missing
default values
Dynamic Data Bugs: Issues in real-time, Static Data Bugs: Issues in fixed, predefined, or
changing data during execution. hardcoded data.
Examples: Examples:
Examples:
Incorrect financial calculations
Wrong stock price updates in a trading app
Examples:
Function receiving null values instead of required parameters
API expecting an integer but receiving a string
Control Bugs: Bugs that affect how data is processed based on logical or conditional errors.
Examples:
Incorrect condition checking for user permissions
Data flow disrupted due to missing validation
4) CODING BUGS:
Coding errors of all kinds can create any of the other kind of bugs.
Syntax errors are generally not important in the scheme of things if the source language
translator has adequate syntax checking.
If a program has many syntax errors, then we should expect many logic and coding bugs.
The documentation bugs are also considered as coding bugs which may mislead the
maintenance programmers.
a) Interface:
External interfaces enable communication between the system and the outside world.
These interfaces include:
Devices (e.g., keyboards, touch screens).
Actuators & Sensors (e.g., temperature sensors, motion detectors).
Input Terminals (e.g., ATMs, POS systems).
Printers (e.g., networked or local).
Communication Lines (e.g., APIs, network protocols)
The primary design goal for an external interface is robustness. Every human or machine interface
should follow a defined protocol.
b) Hardware Architecture:
Bugs related to hardware architecture originate mostly from misunderstanding how the
hardware works.
Examples of hardware architecture bugs: address generation error, i/o device operation /
instruction error, waiting too long for a response, incorrect interrupt handling etc.
The remedy for hardware architecture and interface problems is two fold:
(1) Good Programming and Testing
(2) Centralization of hardware interface software in programs written by hardware
interface specialists.
Program bugs related to the operating system are a combination of hardware architecture
and interface bugs mostly caused by a misunderstanding of what it is the operating system
does.
Use operating system interface specialists, and use explicit interface modules or macros for
all operating system calls.
This approach may not eliminate the bugs but at least will localize them and make testing
easier.
d) Software Architecture:
Memory is subdivided into dynamically allocated resources such as buffer blocks, queue
blocks, task control blocks, and overlay buffers.
External mass storage units such as discs, are subdivided into memory resource pools.
Some resource management and usage bugs: Required resource not obtained, Wrong
resource used, Resource is already in use, Resource dead lock etc
Resource Management Remedies: A design remedy that prevents bugs is always preferable to a test
method that discovers them.
The design remedy in resource management is to keep the resource structure simple: the
fewest different kinds of resources, the fewest pools, and no private resource management.
g) Integration Bugs:
Integration bugs are bugs having to do with the integration of, and with the interfaces
between, working and tested components.
These bugs results from inconsistencies or incompatibilities between components.
The communication methods include data structures, call sequences, registers,
semaphores, and communication links and protocols results in integration bugs.
The integration bugs do not constitute a big bug category(9%) they are expensive category
because they are usually caught late in the game and because they force changes in several
components and/or data structures.
h) System Bugs:
System bugs covering all kinds of bugs that cannot be ascribed to a component or to their
simple interactions, but result from the totality of interactions between many components
such as programs, data, hardware, and the operating systems.
There can be no meaningful system testing until there has been thorough component and
integration testing.
System bugs are infrequent (1.7%) but very important because they are often found only
after the system has been fielded.
Remedies:
Test Debugging: The first remedy for test bugs is testing and debugging the tests. Test
debugging, when compared to program debugging, is easier because tests, when properly
designed are simpler than programs and don’t have to make concessions to efficiency.
Test Quality Assurance: Programmers have the right to ask how quality in independent testing is
monitored.
Test Execution Automation: The history of software bug removal and prevention is
indistinguishable from the history of programming automation aids. Assemblers, loaders,
compilers are developed to reduce the incidence of programming and operation errors. Test
execution bugs are virtually eliminated by various test execution automation tools.
Test Design Automation: Just as much of software development has been automated, much test
design can be and has been automated. For a given productivity rate, automation reduces the
bug count - be it for software or be it for tests
Path Testing is the name given to a family of test techniques based on judiciously selecting a
set of test paths through the program.
If the set of paths are properly chosen then we have achieved some measure of test
thoroughness. For example, pick enough paths to assure that every source statement has
been executed at least once.
Path testing techniques are the oldest of all structural test techniques.
Path testing is most applicable to new software for unit testing. It is a structural technique.
It requires complete knowledge of the program's structure.
It is most often used by programmers to unit test their own code.
The effectiveness of path testing rapidly deteriorates as the size of the software aggregate
under test increases.
The bug assumption for the path testing strategies is that something has gone wrong with
the software that makes it take a different path than intended.
As an example "GOTO X" where "GOTO Y" had been intended.
Structured programming languages prevent many of the bugs targeted by path testing: as a
consequence the effectiveness for path testing for these languages is reduced and for old
code in COBOL, ALP, FORTRAN and Basic, the path testing is indispensable.
CONTROL FLOW GRAPHS:
The control flow graph is a graphical representation of a program's control structure. It uses
the elements named process blocks, decisions, and junctions.
Flow Graph Elements: A flow graph contains four different types of elements.
(2) Decisions
(3) Junctions
1) Process Block:
2) Decisions:
3) Case Statements:
4) Junctions:
A junction is a point in the program where the control flow can merge.
Examples of junctions are: the target of a jump or skip instruction in ALP, a label that is a
target of GOTO.
CONTROL FLOW GRAPHS Vs FLOWCHARTS:
A predicate is a logical condition in a program that controls decision-making, such as if, while, or
for statements. It determines which part of the code executes.
Example:
python
CopyEdit
if (x > 5): # Predicate: (x > 5)
print("x is greater than 5")
2. Path Predicate
A path predicate is a set of logical conditions that must be true to follow a specific execution path in a
program. It is formed by combining predicates along a given path.
Example:
python
CopyEdit
if (x > 5): # Predicate 1
if (y < 10): # Predicate 2
print("Path A")
else:
print("Path B")
else:
print("Path C")
3. Achievable Paths
Achievable paths are execution paths that can actually occur with some input values. Some paths may
be unreachable due to logical constraints or program structure.
python
CopyEdit
if (x > 10): # Predicate 1
if (x < 5): # Predicate 2 (Contradiction: This can never be true)
print("Unreachable Path")
No value of x can be both greater than 10 and less than 5 at the same time.
The path (x > 10) AND (x < 5) is not achievable.
Key Points:
These concepts are crucial for path coverage testing to ensure all possible execution flows are
tested. 🚀
4o
You said:
path sensitizing
ChatGPT said:
Path sensitizing is the process of finding input values that make a specific execution path achievable
in a program. It ensures that a particular path can be executed by satisfying the conditions (predicates)
along the way.
python
CopyEdit
def test_function(x, y):
if x > 5: # Predicate 1
if y < 10: # Predicate 2
print("Path A")
else:
print("Path B")
else:
print("Path C")
Path A: Needs x > 5 and y < 10. Example: (x=6, y=5) ✅ Achievable
Path B: Needs x > 5 and y >= 10. Example: (x=7, y=15) ✅ Achievable
Path C: Needs x ≤ 5. Example: (x=3, y=8) ✅ Achievable
python
CopyEdit
if x > 10:
if x < 5: # Contradiction: No x can be both >10 and <5
print("Unreachable")
Here, the path (x > 10) AND (x < 5) is unachievable, so no input values can satisfy it.
By applying path sensitizing, testers ensure that all feasible paths are tested and identify impossible
paths in the program. 🚀
4o
You said:
path instrumentation
ChatGPT said:
Path Instrumentation is a technique used to track which execution paths are taken during program
execution. It involves inserting instrumentation code into the program to collect information about
executed paths.
if y < 10:
print("Path C taken")
else:
print("Path D taken")
# Test cases
test_function(6, 5) # Executes Path A and Path C
test_function(4, 15) # Executes Path B and Path D
Here, instrumentation is done using print statements to track which paths were taken.
python
CopyEdit
import logging
if y < 10:
logging.info("Path C taken")
else:
logging.info("Path D taken")
This stores path execution logs in a file (path_log.txt) for later analysis.
✅ Confirms that every decision point (if, while, for) and statement is executed at least once.
✅ Helps in branch coverage, statement coverage, and path coverage.
💡 Example:
python
CopyEdit
if x > 10:
if x < 5: # This condition can never be true
print("Unreachable code")
✅ Ensures that loops exit correctly and do not result in infinite execution.
✅ Helps verify loop conditions, edge cases, and boundary values.
python
CopyEdit
i = 1
while i < 10:
print(i)
i += 1 # If this line is missing, it causes an infinite loop
✅ Helps uncover security vulnerabilities by testing all logical paths, including rare ones.
✅ Identifies unexpected execution flows that could lead to exploits.
💡 Example: Testing authentication paths to check all possible login failures and bypasses.