LECTURE 3: WHITE
BOX TESTING &
UNIT TESTING WITH
PYTHON
Learning Objectives
• Explain the concept of white box testing and its
typical application areas
• Design test cases to achieve Statement Coverage
and Branch/Decision Coverage
• Understand the basics of unit testing, its importance,
and scope in software development
• Write simple unit tests using Python's built-in
`unittest` framework
White Box Testing, also known as structural testing or code-
based testing, examines the internal structure & workings of
a system.
What is
White Unlike black box testing, white box testing requires
Box
knowledge of the internal code, logic & infrastructure.
Testing? Its major objective is to focus on the internal program
structure to discover all internal program errors & ensure
thorough path coverage.
White Box Testing: Advantages
and Disadvantages
• Advantages: Ensures thorough path coverage, allowing for the
detection of bugs early in the development cycle.
• Advantages: Optimizes code by revealing hidden errors &
improving security vulnerabilities.
• Disadvantages: Requires programming knowledge, making it
more complex & time-consuming to design tests.
• Disadvantages: Can be more costly due to the expertise and
effort required & may not reveal missing functionalities.
Statement Coverage
• Statement Coverage is a white box testing technique
that ensures every executable statement in the
source code is executed at least once.
• The goal is to cover all lines of code, verifying that no
part of the program remains untested.
• Achieving 100% statement coverage means that
every line has been 'touched' by at least one test
case.
Statement Coverage:
Practical Example
• Consider a simple Python function `calculate_discount(price,
is_member)` to apply a discount.
• To achieve 100% statement coverage, we need test cases that
execute both the `if` and the `else` blocks.
• Test Case 1 (price=100, is_member=True) covers the `if` block;
• Test Case 2 (price=100, is_member=False) covers the `else` block.
'''python
def calculate_discount (price, is_member):
if is_member:
Code discounted_price = price * 0.9
# Statement 1
Example: else:
discounted_price = price
Statement # Statement 2
return discounted_price
Coverage # Statement 3
# Test Case 1: is_member = True
# Expected: 90.0 (Covers Statement 1, 3)
# Test Case 2: is_member = False
# Expected: 100.0 (Covers Statement 2, 3)
'''
Branch/Decision
Coverage
• Branch/Decision Coverage ensures that every
possible outcome of each decision point (e.g.,
`if`, `else`, `while` conditions) in the code is
executed at least once.
• This is a stronger form of coverage than statement
coverage because it verifies both the `true` and
`false` outcomes of every condition.
• A condition like `if (A and B)` requires testing `true`
for `A and B` and `false` for `A and B` (which
could be `not A` or `not B`).
Branch/Decision Coverage:
Comparison & Example
• Statement coverage might pass even if a critical branch is never taken, while branch
coverage explicitly tests both outcomes of a decision.
• For `if (A or B)`: Statement coverage might only need `A=True` to cover the `if`
block. Branch coverage would require `A=True` (or `B=True`) AND `A=False, B=False`
to cover both true and false outcomes.
• Example: For `calculate_discount(price, is_member)`, the previous two test cases
already achieve 100% branch coverage for the `is_member` condition.
Code Example: Branch Coverage
• '''python
def check_eligibility(age, has_license):
if age >= 18 and has_license: # Decision Point
return True # Statement 1
else:
return False # Statement 2
# To achieve 100% Branch Coverage:
# Test Case 1: age=20, has_license=True (Covers True branch of 'and')
# Test Case 2: age=16, has_license=True (Covers False branch, due to age < 18)
# Test Case 3: age=20, has_license=False (Covers False branch, due to not has_license)
# Note: Test Case 2 and 3 together cover the 'False' outcome of the 'and' condition '''
• Unit Testing is a software testing method
where individual units or components of a
software are tested in isolation.
• A 'unit' is the smallest testable part of an
Introduction application, typically a function, method, or
class.
to Unit
Testing • Importance: Isolates bugs early, validates each
unit performs as designed, and provides
documentation for the code's intended behavior.
• Scope: Primarily performed by developers
during the coding phase to ensure their code
works correctly before integration.
• Python's built-in `unittest` module provides
a rich set of tools for constructing and running
Python tests.
• A test case is created by subclassing
`unittest` `[Link]`.
• Methods like `setup ()` & `teardown ()` are
Framework used for initializing and cleaning up resources
before and after each test method,
respectively.
Basics • Assertion methods (e.g., `assertEqual ()`,
`assertTrue ()`, `assertRaises ()`) are used to
check for expected outcomes.
'''python
import unittest
Code def add(a, b):
Example: return a + b
Simple class TestAddFunction([Link]):
`unittest` def setUp(self):
# Optional: Setup resources before each test method
self.a = 5
Structure self.b = 3
def test_add_positive_numbers(self):
[Link](add(self.a, self.b), 8)
'''python
Code def test_add_negative_numbers(self):
[Link](add(-1, -1), -2)
Example: def test_add_zero(self):
[Link](add(5, 0), 5)
Simple def tearDown(self):
`unittest`
# Optional: Clean up resources after each
test method
pass
Structure if __name__ == '__main__':
[Link]()
'''
• Code coverage (e.g., statement, branch)
measures the percentage of code executed by
tests, indicating how much of your code is being
exercised.
Coverage • Testing is the broader activity of verifying
vs. Testing: software correctness, of which coverage is a
metric.
Relationship • High coverage does not guarantee quality; it
& only indicates that more code paths have been
*traversed*, not necessarily *verified* for
Importance correctness.
• Importance: Coverage helps identify untested
areas, guiding developers to write more
comprehensive tests, but it's a tool, not a goal in
itself.
• Tests should be independent, fast, repeatable,
self-validating, and timely (FIRST principles).
Best • Test one thing at a time: Each test method
should focus on a single piece of functionality.
Practices • Avoid testing private methods directly; focus
for Unit
on public interfaces and their behavior.
Testing • Use clear, descriptive test names that explain
what the test is verifying.
• Mock external dependencies to ensure tests
are isolated and run quickly.
• White Box Testing (structural) focuses on the
internal logic and code paths, ensuring internal
mechanisms work as intended.
Connecting • Black Box Testing (functional) focuses on
external behavior and requirements, ensuring
White Box &
the software meets user expectations without
looking at the code.
Black Box • Integration: Unit tests (white box) validate
individual components, while higher-level tests
(black box, like integration/system tests) verify
Techniques how components interact and meet overall
requirements.
• A robust testing strategy combines both:
white box for internal quality and black box for
external correctness.
• White Box Testing examines internal code,
crucial for thoroughness and early bug
detection.
• Statement and Branch Coverage are key
Key metrics for measuring white box test
effectiveness.
Takeaways • Unit Testing validates individual code
units, performed by developers using
frameworks like Python's `unittest`.
• Effective testing combines white box
(internal quality) and black box (external
requirements) approaches.