Sta Unit Ii
Sta Unit Ii
Boundary Value Analysis (BVA) is a software testing technique that focuses on testing the
boundaries of input values to ensure that the software handles edge cases correctly. It is based on
the observation that errors are often more likely to occur at the boundaries of input ranges, rather
than within the middle of those ranges. BVA is particularly useful for validating the input
domain of a system, where you focus on the extremes (edges) of the input range.
Key Concepts:
• Boundary values refer to the values at the edges of input ranges. For example, if a
system expects an input between 1 and 100, the boundary values would be 1, 100, and
values just outside this range (e.g., 0 and 101).
• Boundary Value Analysis tests the following:
o The lower boundary of the input range.
o The upper boundary of the input range.
o Just below the boundaries.
o Just above the boundaries.
• Real-World Errors: Many defects occur at the boundaries (e.g., off-by-one errors). This
method aims to catch those defects early by specifically testing edge cases.
• Test Coverage: BVA helps ensure that the test cases cover the entire range of possible
inputs, including the edge cases that are often prone to failure.
• Efficiency: Instead of testing every possible value within the range, BVA helps you focus
on critical edge cases, optimizing the number of tests needed.
Consider a program that takes an integer input between 1 and 100 (inclusive). The boundaries for
the input are 1 and 100. BVA would guide you to create test cases that focus on these boundary
values, as well as values just outside these limits.
• For example, 50 (mid-range), though it's not the boundary, could be tested to ensure
normal behavior.
Steps for Performing Boundary Value Analysis:
1. Identify Input Ranges: Determine the valid input ranges for the system. For example,
the system might accept input values between 10 and 20.
2. Define Boundaries: Identify the boundary values for each input. For example, for the
input range [10, 20], the boundaries would be 10 and 20.
3. Generate Test Cases: Create test cases for the following values:
o The lower boundary.
o The upper boundary.
o Just below the lower boundary.
o Just above the upper boundary.
4. Execute Tests: Run the tests and verify that the system handles the boundary values
correctly.
Example Scenario:
If the system takes an input for age, and the valid age range is between 18 and 60, the boundaries
would be:
• Lower boundary: 18
• Upper boundary: 60
• Just below lower boundary: 17
• Just above upper boundary: 61
1. Single Value Boundaries: Where the boundary consists of one specific value (e.g., a
number).
o Example: A number between 10 and 20, where 10 and 20 are the boundaries.
2. Range Boundaries: Where the boundary is defined by a range of values.
o Example: An age range between 18 and 65, with boundaries at 18 and 65.
• Detects Edge-Case Bugs: By focusing on the boundaries, BVA can help catch errors that
might not be found by testing only the normal cases.
• Improves Test Coverage: It ensures that all critical parts of the input range are tested.
• Increases Test Efficiency: By narrowing the focus to boundary conditions, BVA helps
optimize the number of test cases needed, without testing every single value within a
range.
Challenges:
• Limited Scope: BVA only focuses on boundary conditions, so it might miss bugs that
exist within the interior of input ranges. This is where Equivalence Partitioning can
complement it.
• Requires Clear Range Definitions: For BVA to be effective, you need to clearly define
valid input ranges, which might not always be obvious in complex systems.
Conclusion:
Boundary Value Analysis is a powerful technique for testing edge cases and ensuring that
software behaves correctly near the boundaries of input ranges. It's often used alongside other
testing techniques, such as Equivalence Partitioning, to create a comprehensive test strategy that
improves the reliability and robustness of the software.
EQUIVALENCE CLASS TESTING
This method reduces the number of test cases while ensuring broad coverage of different input
scenarios, making the testing process more efficient.
1. Equivalence Classes: A set of inputs that the system is expected to treat in the same way.
For example, if a system expects an age input between 18 and 60, you would divide the
inputs into equivalence classes:
o Valid Class: Ages between 18 and 60 (inclusive).
o Invalid Class 1: Ages less than 18.
o Invalid Class 2: Ages greater than 60.
2. Test Case Selection: From each equivalence class, only one representative value is
selected to test, assuming that other values from the class will behave similarly. This
helps reduce the number of test cases while maintaining effective coverage.
3. Classifying Inputs: The first step in equivalence partitioning is identifying the valid and
invalid input ranges, and then dividing those ranges into meaningful partitions or
equivalence classes.
Example:
Suppose you have a system that accepts a number between 10 and 100. You would define the
equivalence classes as follows:
Now, for testing, you don't need to test every number between 10 and 100. You would only test a
value from each equivalence class:
1. Identify Input Parameters: Understand the different types of inputs the system accepts.
These could be numbers, text strings, dates, or anything else the system uses as input.
2. Divide the Inputs into Equivalence Classes:
o Valid Classes: Inputs that should be accepted by the system.
o Invalid Classes: Inputs that should be rejected by the system.
3. Select Test Cases: For each equivalence class, select one value to test. These values
should cover both valid and invalid input spaces.
4. Execute Tests: Run the test cases with the selected representative values and check if the
system behaves as expected.
Let’s say a system accepts an age input between 18 and 60. You would define the equivalence
classes like this:
• Valid Class: Ages 18 to 60, inclusive (e.g., test with age 25).
• Invalid Class 1 (Below range): Ages less than 18 (e.g., test with age 10).
• Invalid Class 2 (Above range): Ages greater than 60 (e.g., test with age 65).
For a system that accepts a string with a length between 5 and 10 characters, the equivalence
classes might be:
1. Valid Equivalence Classes: These contain inputs that should be accepted by the system.
For example, a valid phone number or a valid age range.
2. Invalid Equivalence Classes: These contain inputs that should be rejected by the system.
This could include inputs that fall outside the valid range, contain incorrect formats, or
are otherwise inappropriate for the system’s logic.
• Reduced Test Cases: Since you're testing one representative from each equivalence
class, you dramatically reduce the number of test cases compared to testing every single
value individually.
• Better Test Coverage: It ensures that a wide range of inputs are covered, including
boundary conditions (often complemented with Boundary Value Analysis).
• Efficiency: It’s an efficient way to create test cases that still provide high coverage of the
system's input domain.
• Equivalence Class Testing focuses on dividing the input data into valid and invalid
groups and testing one representative value from each group.
• Boundary Value Analysis focuses specifically on testing the boundaries of input ranges,
since errors often occur near these boundaries.
While they are different techniques, Equivalence Class Testing and Boundary Value Analysis
are complementary and often used together to maximize test coverage and ensure robust testing
of both normal and edge-case scenarios.
While equivalence class testing is a technique and not a tool, many test management tools (like
TestRail or Jira) allow testers to create and manage equivalence classes as part of their test case
design process. Some automated testing tools may also assist in generating test cases based on
equivalence partitions
DECISION TABLE BASED TESTING
• Action Stubs: The lower-left portion listing all possible actions or outcomes that the
system can perform based on the conditions (e.g., "Allow login," "Display error
message").
• Condition Entries: The upper-right portion, where values (e.g., "Yes/No," "True/False,"
or specific values) are specified for each condition across different rules/scenarios.
• Action Entries: The lower-right portion, which indicates with a mark (often an "X" or a
checkmark) which action(s) should be executed for each specific combination of
conditions (rule).
2. Create the Table Structure: List conditions in the top section and actions in the bottom
section.
3. Map All Possible Combinations: List every possible unique combination of conditions
as individual columns (rules). For 'n' binary conditions, there are 2𝑛 combinations.
4. Define Actions for Each Rule: For each combination of conditions, determine the
expected system action(s) and mark the corresponding action entry.
5. Simplify and Validate (Optional but recommended): Remove impossible or
redundant combinations (collapsed decision table testing) and ensure the table
accurately reflects the business logic with stakeholders.
6. Generate Test Cases: Each column/rule in the completed decision table represents a
distinct test case, which can then be used for testing.
CAUSE EFFECT GRAPHING TECHNIQUE
1. Introduction to Cause-Effect Graphing:
• Cause-Effect Graphing is a technique used in software testing to generate test cases
by mapping the relationship between causes (inputs) and effects (outputs).
• This method is particularly useful for small programs where inputs and outputs have
dependencies or logical conditions.
• It considers different combinations of inputs (unlike Boundary Value Analysis or
Equivalence Class Testing, which treat inputs as independent).
2. Key Terms:
• Causes: Inputs to the program, often represented by variables or conditions.
• Effects: Outputs or results produced by the program, based on the causes.
3. Steps for Cause-Effect Graphing:
1. Identify Causes and Effects:
o Use the Software Requirements Specification (SRS) to identify all possible
inputs (causes) and outputs (effects).
2. Design the Cause-Effect Graph:
o Draw a graph showing the relationship between causes and effects using
logical operators like AND, OR, NOT, etc.
3. Apply Constraints:
o Add constraints (such as Exclusive, Requires, etc.) to the graph to represent
real-world conditions or restrictions among the causes.
4. Create a Decision Table:
o Convert the cause-effect graph into a decision table. Each column of the
decision table represents a test case.
5. Generate Test Cases:
o Use the decision table to generate individual test cases, reducing unnecessary
columns if possible.
4. Types of Relationships:
• Identity: This function states that if c1 is 1, then e1 is 1; else e1 is 0
• NOT: This function states that if c1 is 1, then e1 is 0; else e1 is 1.
• AND: This function states that if both c1 and c2 are 1, then e1 is 1; else e1 is 0
• OR: This function states that if either c1 or c2 is 1, then e1 is 1; else e1 is 0.
5. Types of Constraints:
• Exclusive (E): At most one cause can be true (both cannot be true at the same time).
• Inclusive (I): At least one cause must always be true (both can be true together).
• One and Only One (O): Exactly one of the causes must be true.
• Requires (R): For one cause to be true, another cause must also be true.
• Mask: If one effect is true, it forces another effect to be false.
6. Applicability:
• Cause-Effect Graphing is a systematic method of generating test cases, considering
dependencies between inputs using constraints.
• It is effective for small programs but may become too complex for large programs
6. Example:
A tourist of age greater than 21 years and having a clean driving record is supplied a rental
car. A premium amount is also charged if the tourist is on business, otherwise it is not
charged. If the tourist is less than 21 year old, or does not have a clean driving record, the
system will display the following message: “Car cannot be supplied” Draw the cause-effect
graph and generate test cases.
Solution:
The causes are
c1 : Age is over 21
c2 : Driving record is clean
c3 : Tourist is on business
and effects are
e1 : Supply a rental car without premium charge.
e2 : Supply a rental car with premium charge
e3 : Car cannot be supplied
The cause-effect graph is shown in Figure 2.15 and decision table is shown in Table 2.45. The
test cases for the problem are given in Table 2.46
CONTROL FLOW TESTING
Control Flow Testing is a type of white-box testing technique that focuses on the control flow
of a program, ensuring that all possible execution paths and logical branches are tested. It
involves analyzing the program’s source code to identify the flow of control, then designing test
cases that cover different execution paths, branches, and loops in the code. The goal is to ensure
that every possible condition and decision point is tested, and any logical errors or missed
branches are identified.
Key Concepts:
1. Statement Coverage: Ensures that every statement in the code is executed at least once.
The goal is to cover every line of code during testing.
2. Branch Coverage: Ensures that every possible branch (decision point) in the code is
executed. For example, every if and else branch should be tested at least once.
3. Path Coverage: Ensures that every possible execution path through the program is
tested. This covers combinations of decisions, loops, and branches, ensuring that all
logical paths are evaluated.
4. Condition Coverage: Ensures that each individual condition in decision points (such as
the conditions inside if statements) evaluates to both true and false at least once. This
helps catch logical errors where a single condition might be incorrectly implemented.
5. Multiple Condition Coverage: A stronger form of condition coverage that ensures all
possible combinations of conditions are evaluated. This is useful for complex decision
points with multiple conditions (e.g., in compound if statements).
Consider the following simple code snippet with an if statement and a loop:
Possible paths:
1. Statement Coverage:
o Aim: Ensure that each line of code is executed at least once.
o Example: Test the if condition, else condition, and the return statement.
2. Branch Coverage:
o Aim: Ensure that each branch (decision point) is tested. This means testing both
the True and False outcomes of every decision point.
o Example: In the above code, test for both order_value > 100 (True) and
order_value <= 100 (False).
3. Path Coverage:
o Aim: Ensure that every possible path through the program is tested. This might
involve testing combinations of branches and loops to cover all execution
scenarios.
o Example: Test the combinations of order_value and customer_type to ensure
all paths are exercised.
4. Condition Coverage:
o Aim: Ensure that each condition in a decision point is evaluated to both true and
false at least once.
o Example: Test cases where order_value > 100 and customer_type == 'VIP'
(True), and test cases where order_value <= 100 and customer_type !=
'VIP' (False).
5. Multiple Condition Coverage:
o Aim: Ensure that every combination of conditions (for example, conditions in
compound if statements) is tested.
o Example: If there are two conditions in an if statement, ensure you test both
True/True, True/False, False/True, and False/False combinations.
1. Thoroughness: By testing all execution paths, control flow testing ensures that all parts
of the program are exercised and checked for correctness.
2. Helps Identify Logical Errors: It helps uncover logical errors or flaws in branching
logic that may not be detected by other testing methods like functional testing.
3. Detects Dead Code: Since every branch and path is examined, it can help identify
unreachable or "dead" code that is not executed by any input or condition.
4. Improves Code Quality: By ensuring that all parts of the code are covered and executed,
it can lead to cleaner, more reliable code.
Challenges of Control Flow Testing:
1. JUnit/Mockito: These testing frameworks can help automate control flow tests in Java,
including branching logic and coverage measurement.
2. Cobertura: A tool for measuring code coverage in Java programs, helping identify
which parts of the code are covered by control flow testing.
3. Emma: Another code coverage tool for Java, useful for measuring statement, branch, and
path coverage.
4. [Link]: A tool for measuring code coverage in Python, which can help with
control flow testing in Python programs.
PATH TESTING
Path Testing is a white-box testing technique that focuses on executing different paths through
the program’s control flow. The goal of path testing is to ensure that all possible execution paths
in the program are tested. By doing so, you can validate that the system behaves correctly in
various scenarios, including both typical and edge cases.
Let’s consider the following example of a simple function that computes whether a person is
eligible for a loan:
Start --> [Check age] --> [Check income] --> [Eligible] --> End
| |
[Not eligible] [Not eligible]
For full path coverage, you need to test each path at least once. The test cases might look like
this:
• Test Case 1: age = 16, income = 30000 (Path 1: age < 18)
o Expected Output: "Not eligible"
• Test Case 2: age = 20, income = 20000 (Path 2: age >= 18, income < 25000)
o Expected Output: "Not eligible"
• Test Case 3: age = 25, income = 30000 (Path 3: age >= 18, income >= 25000)
o Expected Output: "Eligible"
You run each test case, checking that each decision point and path is correctly exercised.
CYCLOMATIC COMPLEXITY
Cyclomatic Complexity is a metric that helps in determining the number of linearly independent
paths in a program, and thus, it gives an estimate of the minimum number of test cases required
for full path coverage.
V(G)=E−N+2PV(G) = E - N + 2PV(G)=E−N+2P
Where:
This tells us that there are 3 independent paths, and we would need at least 3 test cases to
achieve full path coverage.
1. Thorough Testing: Path testing ensures that all possible execution paths are tested,
which improves the chances of finding errors, especially logical flaws that occur under
specific conditions.
2. Helps Detect Logical Errors: It helps identify errors in decision-making, loops, and
combinations of conditions that may not be caught by other testing techniques like
boundary value analysis or equivalence partitioning.
3. Quantifiable Coverage: The use of cyclomatic complexity provides a quantitative
measure of the program's complexity and the minimum number of test cases required for
full path coverage.
4. Improves Code Quality: By testing all paths, path testing ensures that all the code is
executed, which can help find dead code, uninitialized variables, and unreachable
branches.
1. Combinatorial Explosion: As the program becomes more complex with more decision
points, the number of paths can grow exponentially, leading to a large number of test
cases. This is known as combinatorial explosion and can make path testing infeasible
for large systems.
2. Time and Effort: Because path testing requires covering many paths, it can be time-
consuming and resource-intensive, especially in large programs with complex control
flow.
3. Not Always Practical for Large Programs: In complex systems, achieving complete
path coverage may not be feasible. In such cases, testing strategies like branch coverage
or code coverage may be used as approximations.
1. JUnit (for Java): Can be used for unit testing and can help measure the coverage of
paths in Java code.
2. PyTest (for Python): Can be used for running tests and with coverage plugins to
measure path coverage.
3. Cobertura: A code coverage tool for Java that measures various levels of code coverage,
including path coverage.
4. Emma: Another Java code coverage tool that provides detailed reports on path coverage.
Conclusion:
Path testing is a highly effective technique to ensure that all logical paths through a program are
tested. It helps in identifying bugs, logical errors, and dead code, leading to higher software
quality. However, due to the combinatorial explosion of paths in complex programs, path testing
can be resource-intensive and may require the use of tools to manage and measure coverage
effectively.
DATA FLOW TESTING
Data Flow Testing is a white-box testing technique that focuses on the flow of data within a
program. It aims to examine how data is defined, used, and modified throughout the program,
with a focus on variables, their initialization, and how they are propagated through different
operations. The goal is to ensure that data is handled correctly and that no errors occur in the way
data moves or is manipulated within the system.
1. Data Definition (Def): When a variable is assigned a value for the first time (e.g., x =
5).
2. Data Use (Use): When a variable is read or utilized in a computation (e.g., y = x + 3).
3. Variable: A storage location in the program that holds data.
4. Control Flow: The sequence of operations that occur based on the program’s control
structures (if-else, loops, etc.).
The primary goal of data flow testing is to identify and validate different data flow paths in the
program, ensuring that variables are used correctly after they are defined and are not used before
being properly initialized. Specifically, the technique focuses on:
1. Definitions:
o total_price is defined first as price - discount.
o total_price is redefined as total_price + (total_price * tax_rate).
2. Uses:
o The first use of total_price occurs when it’s used in the second calculation:
total_price + (total_price * tax_rate).
o The second use is when the function returns total_price.
Test Cases:
1. Test Case 1:
o Input: price = 100, discount = 10, tax_rate = 0.1
o Expected Output: total_price = 100 - 10 + ((100 - 10) * 0.1) = 90 +
9 = 99
2. Test Case 2:
o Input: price = 50, discount = 5, tax_rate = 0.2
o Expected Output: total_price = 50 - 5 + ((50 - 5) * 0.2) = 45 + 9 =
54
1. Detects Initialization Errors: One of the main goals of data flow testing is to ensure that
variables are initialized before use. It can identify errors where variables are read or used
before being given a valid value.
2. Improves Code Quality: By focusing on the way data flows and is manipulated
throughout the system, data flow testing can help improve the correctness and reliability
of the software.
3. Dead Code Detection: Data flow testing can also uncover unused or dead variables that
may not be necessary, leading to cleaner and more maintainable code.
4. Helps Identify Unreachable Code: It can identify scenarios where certain code is never
executed because variables aren't used as expected, helping remove unnecessary code.
1. Complexity for Large Programs: As programs grow larger, the number of variables,
definitions, and uses increases, making it difficult to track data flow manually. This can
lead to complex graphs that are hard to manage.
2. Inadequate for Simple Applications: For simple applications, data flow testing might
be overkill. It’s best suited for programs with complex data manipulations and
interactions.
3. Combinatorial Explosion: Similar to control flow testing, data flow testing can suffer
from combinatorial explosion when there are many variables with multiple definitions
and uses, leading to a large number of test cases.
• Junit (for Java): Can be used in combination with tools to analyze data flow for Java
applications.
• Pytest (for Python): Allows for unit testing and can be extended with tools for data flow
testing.
• Code Coverage Tools: Tools like Jacoco (for Java) or [Link] (for Python) can
help track data flow coverage by measuring which definitions and uses have been tested.
• Static Analysis Tools: Tools like SonarQube and FindBugs can help analyze data flow
statically and highlight potential issues with uninitialized variables or unused code.
Conclusion:
Data Flow Testing is an essential technique, particularly for programs with complex data
interactions, where it's crucial to verify that data is defined, used, and modified correctly. By
focusing on the flow of data through the program, you can detect errors such as uninitialized
variables, improper data usage, and dead code. Although it can be complex and difficult to
manage in large systems, data flow testing is powerful for ensuring that software handles data
correctly and behaves as expected.
MUTATION TESTING
Mutation testing is a software testing technique used to evaluate the effectiveness of a test
suite. It involves introducing small changes (mutations) to the code and checking if the
existing tests can detect these changes. The main goal is to ensure that the tests are robust
enough to catch bugs or unintended behaviors. Following steps are involved in mutation
testing -
Example:
If your tests do not catch many of the mutants, it suggests that your tests are not exhaustive
enough and need improvement to cover more edge cases or subtle issues.
• Improves Test Quality: It helps identify weaknesses in the test suite, like missing
edge cases or unnecessary assumptions.
• Early Bug Detection: Since you’re actively introducing potential errors into the
code, you get a direct indication of where the tests might fail or miss bugs.
• Provides Metrics: It offers a quantifiable measure (mutation score) of the
effectiveness of your tests.
Challenges:
• Computational Cost: Mutation testing can be slow because it requires running the
test suite multiple times (once for each mutant).
• High Number of Mutants: For complex code, the number of possible mutants can
grow large, making it impractical to test them all efficiently.
Tools:
• Equivalence Partitioning (EP): This technique divides the input data into partitions or
equivalence classes. Each class is assumed to be processed similarly, so testing one value
from a class is sufficient.
• Boundary Value Analysis (BVA): Focuses on testing the boundaries of these partitions
because boundary values are often where errors occur.
Key Difference: While equivalence partitioning tests values within a range, boundary value
analysis specifically targets the edges of these ranges (and values just outside them).