Test Design Techniques: Static and Code Analysis π
The Story of the Bug Detective Agency π΅οΈ
Imagine you run a Bug Detective Agency. Your job? Find problems in buildings before people move in. You have two ways to work:
- Walk around and look (without touching anything) β this is Static Testing
- Actually live in the house and use everything β this is Dynamic Testing
Both are super important! Letβs explore how testers become the best bug detectives in the software world.
π Static Testing Overview
What Is Static Testing?
Think of it like reading a recipe before cooking. You donβt turn on the stove yet. You just read and check:
- βWait, it says add 10 cups of salt. Thatβs way too much!β
- βHmm, step 3 comes before step 2. Thatβs wrong!β
Static testing = Finding bugs WITHOUT running the code.
graph TD A["π Code Written"] --> B["π Review & Inspect"] B --> C{Found Bugs?} C -->|Yes| D["π§ Fix Now"] C -->|No| E["β Ready for Next Step"] D --> B
Why Is Static Testing Awesome?
| Benefit | Why It Matters |
|---|---|
| π Fast | Find bugs before running anything |
| π° Cheap | Fixing early costs less |
| π― Catches logic errors | Spots wrong thinking |
| π Improves understanding | Everyone learns the code |
Simple Example
Bad Code (spotted statically):
def divide(a, b):
return a / b # What if b is 0?
A reviewer reads this and says: βHey, what happens if someone passes 0 for b? It will crash!β
Fixed:
def divide(a, b):
if b == 0:
return "Cannot divide by zero!"
return a / b
No code was run. The bug was found just by reading!
π Dynamic Testing
What Is Dynamic Testing?
Now you actually cook the recipe. Turn on the stove. Mix ingredients. Taste the food!
Dynamic testing = Running the code and checking if it works correctly.
graph TD A["π Run the Program"] --> B["π₯ Give It Input"] B --> C["π€ Check Output"] C --> D{Correct?} D -->|Yes| E["β Test Passed"] D -->|No| F["π Bug Found!"]
Real-Life Example
You have a login page. Dynamic testing means:
- Type a username and password
- Click the login button
- See what happens
| Test Case | Input | Expected Result | Actual Result |
|---|---|---|---|
| Valid login | user: βsamβ, pass: β1234β | Welcome Sam! | Welcome Sam! β |
| Wrong password | user: βsamβ, pass: βwrongβ | Error message | Error message β |
| Empty fields | user: ββ, pass: ββ | Please fill all fields | Crashed! π |
Static vs Dynamic: The Difference
Think of buying a used car:
| Static Testing | Dynamic Testing |
|---|---|
| π Look at the car | π Test drive the car |
| Read the manual | Feel how it drives |
| Check for rust spots | Hear if engine sounds weird |
| Look at oil color | See if brakes work |
You need BOTH! Looking finds some problems. Driving finds others.
π¬ Static Code Analysis
What Is Static Code Analysis?
Imagine you have a robot helper that reads your code and says:
- βHey, you made a spelling mistake in this variable!β
- βThis line will never run!β
- βYou forgot to close the file!β
Static Code Analysis = Using tools to automatically scan code for problems.
graph TD A["π Your Code"] --> B["π€ Analysis Tool"] B --> C["π Report"] C --> D["β οΈ Warnings"] C --> E["β Errors"] C --> F["π‘ Suggestions"]
What Do These Tools Find?
| Problem Type | Example | Why Itβs Bad |
|---|---|---|
| Unused variables | x = 5 (never used) |
Wastes memory, confuses readers |
| Dead code | Code after return |
Never runs, misleading |
| Security issues | Using eval() |
Hackers can attack |
| Style problems | Inconsistent spacing | Hard to read |
Popular Static Analysis Tools
| Language | Tool | What It Catches |
|---|---|---|
| Python | PyLint, Flake8 | Style, errors, complexity |
| JavaScript | ESLint | Bugs, bad practices |
| Java | SonarQube | Security, bugs, smells |
| C/C++ | Cppcheck | Memory leaks, bugs |
Simple Example
Code with issues:
function greet(name) {
var unused = 42;
console.log("Hello " + nme);
return;
console.log("Goodbye");
}
Static analyzer output:
Line 2: 'unused' is declared but never used
Line 3: 'nme' is not defined (did you mean 'name'?)
Line 5: Unreachable code after return
You havenβt run the code, but the tool found 3 bugs!
π₯ Code Review Techniques
What Is Code Review?
Remember group projects at school? Before submitting, your friend reads your work and says:
- βThis sentence doesnβt make sense.β
- βYou repeated this paragraph twice.β
- βThis answer is wrong!β
Code Review = Team members read each otherβs code to find problems and share knowledge.
graph TD A["π¨βπ» Developer Writes Code"] --> B["π€ Submits for Review"] B --> C["π Reviewer Reads Code"] C --> D{Approved?} D -->|No| E["π¬ Comments & Suggestions"] E --> F["π§ Developer Fixes"] F --> B D -->|Yes| G["β Code Merged"]
Types of Code Review
| Type | How It Works | Best For |
|---|---|---|
| Pair Programming | Two people code together | Learning, complex problems |
| Over-the-Shoulder | Reviewer watches coder | Quick informal checks |
| Tool-Based (Pull Request) | Review on GitHub/GitLab | Remote teams, async work |
| Formal Inspection | Scheduled meetings, checklists | Critical systems |
What Reviewers Look For
- Correctness β Does it work right?
- Readability β Can others understand it?
- Efficiency β Is it fast enough?
- Security β Is it safe from hackers?
- Standards β Does it follow team rules?
Example Code Review Comment
Original code:
def calc(x, y, z):
return x + y * z
Reviewer comments:
π¬ βWhat do x, y, and z represent? Please use meaningful names like
base,rate,hours. Also, should multiplication happen before addition? Add parentheses to make it clear:base + (rate * hours)β
Improved code:
def calculate_pay(base, rate, hours):
return base + (rate * hours)
Code Review Checklist Example
- [ ] Does the code do what itβs supposed to?
- [ ] Are variable names clear?
- [ ] Are there any bugs I can spot?
- [ ] Is there duplicate code that could be simplified?
- [ ] Are error cases handled?
- [ ] Is it easy to read and understand?
𧬠Mutation Testing
What Is Mutation Testing?
Hereβs a fun story! Imagine you have a guard dog. You want to know: Is this dog actually good at catching burglars?
So you send fake burglars! If the dog catches them, itβs a good dog. If fake burglars sneak past, your dog needs training.
Mutation Testing = Making tiny changes to your code (mutations) to see if your tests catch them.
graph TD A["π Original Code"] --> B["𧬠Create Mutants"] B --> C["πΎ Mutant 1"] B --> D["πΎ Mutant 2"] B --> E["πΎ Mutant 3"] C --> F["π§ͺ Run Tests"] D --> F E --> F F --> G{Test Failed?} G -->|Yes| H["β Mutant Killed - Good Test!"] G -->|No| I["π± Mutant Survived - Weak Test!"]
How Mutation Testing Works
Step 1: Start with working code and a test
# Original code
def is_adult(age):
return age >= 18
# Test
def test_is_adult():
assert is_adult(20) == True
Step 2: Create mutants (tiny changes)
| Mutant | Change Made | New Code |
|---|---|---|
| Mutant 1 | Change >= to > |
return age > 18 |
| Mutant 2 | Change >= to <= |
return age <= 18 |
| Mutant 3 | Change 18 to 17 |
return age >= 17 |
Step 3: Run tests against each mutant
| Mutant | Test Result | Verdict |
|---|---|---|
| Mutant 1 | is_adult(20) still returns True |
π± SURVIVED! |
| Mutant 2 | is_adult(20) returns False, test fails |
β KILLED! |
| Mutant 3 | is_adult(20) still returns True |
π± SURVIVED! |
Problem found! Two mutants survived. Our test isnβt good enough!
Better test:
def test_is_adult():
assert is_adult(20) == True
assert is_adult(18) == True # Boundary!
assert is_adult(17) == False # Below boundary!
Now all mutants would be killed!
Mutation Score
Mutation Score = (Killed Mutants / Total Mutants) Γ 100%
| Score | Meaning |
|---|---|
| 100% | Perfect! All mutants caught |
| 80%+ | Good test suite |
| 60-80% | Needs improvement |
| Below 60% | Tests are too weak |
Common Mutation Types
| Mutation Type | Original | Mutated |
|---|---|---|
| Arithmetic | a + b |
a - b |
| Relational | x > y |
x >= y |
| Logical | a && b |
a || b |
| Constant | return 0 |
return 1 |
| Negation | if (x) |
if (!x) |
π― Bringing It All Together
Hereβs how all these techniques work together in the real world:
graph TD A["π¨βπ» Developer Writes Code"] --> B["π€ Static Analysis"] B --> C["π₯ Code Review"] C --> D["π§ͺ Write Tests"] D --> E["π Dynamic Testing"] E --> F["𧬠Mutation Testing"] F --> G{All Good?} G -->|Yes| H["π Deploy!"] G -->|No| A
Quick Comparison
| Technique | When | Who/What | Finds |
|---|---|---|---|
| Static Testing | Before running | Humans reading | Logic errors, design flaws |
| Dynamic Testing | While running | Running the code | Runtime bugs, crashes |
| Static Code Analysis | Anytime | Automated tools | Style, security, common bugs |
| Code Review | Before merging | Team members | All kinds of issues |
| Mutation Testing | After writing tests | Special tools | Weak tests |
π Key Takeaways
- Static Testing is like proofreading β find mistakes without running code
- Dynamic Testing is like test-driving β run the code and check it works
- Static Code Analysis uses robots to scan your code automatically
- Code Review is teamwork β humans checking each otherβs work
- Mutation Testing tests your tests β makes sure your safety net has no holes
Remember: Great testers use ALL these tools! Like a detective using magnifying glass, fingerprint kit, AND asking witnesses. Each technique catches different types of bugs.
Youβre now ready to be a Bug Detective! Go find those bugs before users do! π
