ISTQB Certifications
Technical Test Analyst (CTAL-TTA)
Static and Dynamic Analysis

CTAL-TTA Static and Dynamic Analysis: Code Quality and Runtime Testing

Parul Dhingra - Senior Quality Analyst
Parul Dhingra13+ Years ExperienceHire Me

Senior Quality Analyst

Updated: 1/25/2026

Static and dynamic analysis are complementary techniques that examine software quality from different perspectives. Static analysis inspects code without executing it, finding structural issues and potential defects early. Dynamic analysis examines the running application, detecting runtime behaviors like memory leaks and performance bottlenecks that only manifest during execution.

Mastering both approaches is essential for the CTAL-TTA certification and for technical test analysts who need to ensure code quality at every level.

Understanding Static vs Dynamic Analysis

Key Differences

AspectStatic AnalysisDynamic Analysis
ExecutionCode NOT executedCode IS executed
TimingBefore/during compileDuring runtime
Defects foundStructural, syntactic, potential bugsActual runtime behaviors
CoverageEntire codebaseOnly executed paths
SpeedGenerally fasterRequires test execution
False positivesHigher rateLower (actual observation)

When to Use Each

Static Analysis is Best For:

  • Finding coding standard violations
  • Detecting potential null pointer dereferences
  • Identifying security vulnerabilities in code patterns
  • Measuring code complexity
  • Ensuring consistent code style
  • Finding dead/unreachable code

Dynamic Analysis is Best For:

  • Detecting memory leaks
  • Finding race conditions
  • Measuring actual performance
  • Identifying resource bottlenecks
  • Validating error handling behavior
  • Testing concurrent code

Complementary Approaches: Neither technique alone provides complete coverage. Static analysis finds issues that might never occur at runtime; dynamic analysis finds issues that static analysis cannot predict. Professional testing strategies use both.

Static Code Analysis Fundamentals

Types of Static Analysis

1. Control Flow Analysis

Examines the order of statement execution:

def process_data(data):
    if data is None:
        return None
    result = transform(data)
    return result
    print("Done")  # Unreachable code detected!

Detects:

  • Unreachable code
  • Infinite loops
  • Missing return statements
  • Dead code after unconditional returns/breaks

2. Data Flow Analysis

Tracks how data moves through the program:

def calculate_total(items):
    total = 0
    for item in items:
        subtotal = item.price * item.quantity
        # Bug: total never updated!
    return total  # Always returns 0

Detects:

  • Uninitialized variables
  • Unused variables (defined but never read)
  • Variables overwritten without being used
  • Potential null/undefined dereferences

3. Compliance Analysis

Checks code against coding standards:

// Style violation: Class name should be PascalCase
public class myClass {  // Should be: MyClass
 
    // Violation: Magic number
    public static final int TIMEOUT = 30;  // OK
    int delay = 30;  // Violation: use constant
}

Standards Checked:

  • Naming conventions
  • Code formatting
  • Documentation requirements
  • Language-specific best practices

What Static Analysis Cannot Find

Limitations:

  • Runtime-dependent behavior
  • Actual performance issues
  • Timing-dependent bugs (race conditions)
  • Environment-specific problems
  • Business logic correctness
def get_user_age(user_id):
    user = database.fetch_user(user_id)
    return user.age  # Static analysis: looks fine
                     # Runtime: user could be None!

Static analysis sees the code structure but cannot predict that fetch_user might return None for invalid IDs.

Static Analysis Tool Categories

Linting Tools

Focus on code style and simple error patterns:

LanguageTools
PythonPylint, Flake8, Ruff
JavaScriptESLint, JSHint
JavaCheckstyle, PMD
C/C++cpplint, cppcheck
Gogolint, staticcheck

Example ESLint Rule Violations:

// Rule: no-unused-vars
const unusedVariable = 5;  // Warning!
 
// Rule: eqeqeq (require strict equality)
if (value == null) { }  // Use === instead
 
// Rule: no-var
var oldStyle = true;  // Use let or const

Security Analysis Tools (SAST)

Static Application Security Testing finds vulnerabilities:

ToolFocus
SonarQubeMulti-language, comprehensive
CheckmarxEnterprise security scanning
FortifyEnterprise SAST
Snyk CodeDeveloper-focused
SemgrepCustomizable patterns

Security Issues Detected:

# SQL Injection vulnerability
def get_user(user_input):
    query = "SELECT * FROM users WHERE id = " + user_input  # DANGER!
    # Should use: query = "SELECT * FROM users WHERE id = %s", (user_input,)
 
# Path Traversal vulnerability
def read_file(filename):
    with open("/data/" + filename) as f:  # DANGER!
        return f.read()
    # Attack: filename = "../../../etc/passwd"
 
# Hardcoded credentials
password = "admin123"  # DANGER: Hardcoded secret!

Comprehensive Analysis Platforms

SonarQube Categories:

CategoryDescription
BugsCode that is demonstrably wrong
VulnerabilitiesSecurity weaknesses
Code SmellsMaintainability issues
Security HotspotsRequire manual review
Technical DebtTime to fix issues

Code Metrics and Complexity Analysis

Cyclomatic Complexity

Definition: Measures the number of independent paths through code.

Calculation Methods:

Method 1: Decision Points

V(G) = Number of decision points + 1

Decision points: if, while, for, case, catch, &&, ||, ?:

Method 2: Control Flow Graph

V(G) = E - N + 2P

Where:
E = Edges (flow connections)
N = Nodes (statements)
P = Connected components (usually 1)

Practical Example:

def process_order(order):          # Node 1
    if order.total > 1000:          # Decision 1
        if order.is_member:         # Decision 2
            apply_premium_discount()
        else:
            apply_standard_discount()
    elif order.total > 500:         # Decision 3 (elif counts)
        apply_small_discount()
 
    if order.expedited:             # Decision 4
        add_expedited_shipping()
 
    return finalize_order()         # Node N

Complexity: 4 decisions + 1 = 5

Complexity Risk Levels

ComplexityRisk LevelTesting Implication
1-10LowEasy to test and maintain
11-20ModerateMore testing effort needed
21-50HighDifficult to test, consider refactoring
51+Very HighUntestable, refactoring mandatory
⚠️

Exam Point: Cyclomatic complexity equals the minimum number of test cases required for 100% branch coverage. A function with complexity 15 needs at least 15 tests for full branch coverage.

Other Important Metrics

Lines of Code (LOC) Metrics:

MetricDescription
Physical LOCTotal lines including blanks and comments
Logical LOCExecutable statements only
SLOCSource lines of code
CLOCComment lines of code

Coupling Metrics:

MetricMeaningTarget
Afferent coupling (Ca)Classes that depend on thisLow = stable
Efferent coupling (Ce)Dependencies of this classLow = independent
Instability (I)Ce/(Ca+Ce)0 = stable, 1 = unstable

Cohesion Metrics:

MetricDescription
LCOMLack of Cohesion of Methods
High LCOMClass does too many things
Low LCOMClass is focused (good)

Halstead Complexity Measures

Based on operators and operands:

MeasureFormulaMeaning
Vocabulary (n)n1 + n2Unique operators + operands
Length (N)N1 + N2Total operators + operands
Volume (V)N × log2(n)Size of implementation
Difficulty (D)(n1/2) × (N2/n2)Error proneness
Effort (E)D × VImplementation effort

Code Review Techniques

Types of Reviews

Review TypeFormalityParticipantsDocumentation
InspectionHigh3-6 people, defined rolesFormal reports
Technical ReviewMediumPeers, moderatorIssue tracking
WalkthroughMediumAuthor presentsMeeting notes
Pair ProgrammingLowTwo developersCode itself
Desk CheckLowIndividualInformal notes

Technical Review Focus Areas for TTAs

Code Correctness:

# Review: Is the boundary condition correct?
def is_valid_age(age):
    return age >= 0 and age <= 120  # Should 120 be included?
                                     # What about age exactly 0?

Error Handling:

# Review: Are all failure modes handled?
def fetch_user_data(user_id):
    try:
        response = api.get_user(user_id)
        return response.json()
    except RequestException:
        return None  # Is None appropriate? Log the error?
                     # What about timeout vs connection error?

Resource Management:

# Review: Are resources properly released?
def process_file(path):
    f = open(path)  # File handle leak if exception occurs
    data = f.read()
    f.close()
    return process(data)
 
# Better:
def process_file(path):
    with open(path) as f:  # Guaranteed cleanup
        return process(f.read())

Review Checklists

Security Review Checklist:

[ ] Input validation present for all external data
[ ] SQL queries use parameterized statements
[ ] No hardcoded credentials or secrets
[ ] Sensitive data encrypted in transit and at rest
[ ] Authentication checks on all protected endpoints
[ ] Authorization verified before sensitive operations
[ ] Error messages don't expose internal details
[ ] Logging doesn't include sensitive data

Performance Review Checklist:

[ ] N+1 query patterns avoided
[ ] Appropriate caching implemented
[ ] Large collections processed in batches
[ ] Database indexes used for frequent queries
[ ] Expensive operations happen asynchronously
[ ] Connection pooling configured
[ ] Memory allocation optimized for hot paths

Dynamic Analysis Fundamentals

What Dynamic Analysis Detects

Runtime-Only Issues:

Issue TypeExample
Memory leaksAllocated memory never freed
Race conditionsTwo threads accessing shared data
DeadlocksThreads waiting for each other
Buffer overflowsWriting beyond array bounds
Use-after-freeAccessing freed memory
Resource exhaustionFile handles, connections leaked

Dynamic Analysis Techniques

1. Instrumentation

Adding monitoring code to track behavior:

# Manual instrumentation example
def original_function(x):
    return x * 2
 
def instrumented_function(x):
    start_time = time.time()
    call_count[function_name] += 1
    result = x * 2
    execution_time = time.time() - start_time
    log_metrics(function_name, execution_time)
    return result

2. Sampling

Periodically capturing program state:

  • Less overhead than full instrumentation
  • Statistical picture of behavior
  • Used by many profilers

3. Tracing

Recording detailed execution history:

  • Function calls and returns
  • System calls
  • Memory allocations

Memory Analysis and Leak Detection

Types of Memory Issues

Memory Leak:

void process_requests() {
    while (running) {
        Request* req = malloc(sizeof(Request));
        handle_request(req);
        // Missing: free(req);  <- Memory leak!
    }
}

Use-After-Free:

char* create_message() {
    char* msg = malloc(100);
    strcpy(msg, "Hello");
    free(msg);
    return msg;  // DANGER: returning freed memory!
}

Buffer Overflow:

void copy_name(char* input) {
    char buffer[10];
    strcpy(buffer, input);  // DANGER: no bounds checking!
    // If input > 10 chars, overflows buffer
}

Memory Analysis Tools

ToolPlatformFeatures
ValgrindLinuxMemory leaks, invalid access
AddressSanitizerGCC/ClangFast memory error detection
Dr. MemoryWindows/LinuxMemory debugging
PurifyCommercialComprehensive analysis
Visual StudioWindowsDebug heap analysis

Valgrind Example Output:

==12345== HEAP SUMMARY:
==12345==     in use at exit: 1,024 bytes in 4 blocks
==12345==   total heap usage: 100 allocs, 96 frees
==12345==
==12345== 1,024 bytes in 4 blocks are definitely lost
==12345==    at 0x4C2FB0F: malloc
==12345==    by 0x401234: process_request (server.c:45)
==12345==    by 0x401456: main (server.c:78)

Memory Analysis in Managed Languages

Java/JVM memory issues:

// Memory leak via static collection
public class Cache {
    private static Map<String, Object> cache = new HashMap<>();
 
    public void addToCache(String key, Object value) {
        cache.put(key, value);  // Never removed = memory leak!
    }
}
 
// Memory leak via unclosed resources
public void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    // Processing...
    // Missing fis.close() = resource leak
}

Tools for JVM:

  • JProfiler
  • YourKit
  • VisualVM
  • Eclipse Memory Analyzer (MAT)

Garbage Collection Misconception: Managed languages with garbage collection still have memory leaks. If objects remain reachable (even unintentionally), they won't be collected. Common causes: static collections, listeners not unregistered, caches without eviction.

Performance Profiling

Types of Profilers

CPU Profilers:

  • Identify slow functions
  • Find CPU hotspots
  • Measure function call counts

Memory Profilers:

  • Track allocations
  • Find memory hotspots
  • Analyze object lifecycle

I/O Profilers:

  • Monitor file operations
  • Track network calls
  • Measure database queries

Profiling Metrics

MetricDescriptionAction if High
Self TimeTime in function onlyOptimize algorithm
Total TimeSelf + called functionsCheck call hierarchy
Call CountHow often calledReduce calls, cache
Allocation RateMemory per secondReduce allocations

Interpreting Profiler Output

Function                  Self Time   Total Time   Calls
--------------------------------------------------------
main()                       0.1%       100.0%        1
  process_orders()           0.5%        85.0%        1
    validate_order()         2.0%        30.0%    10000
      check_inventory()     25.0%        25.0%    10000  <- HOTSPOT!
    calculate_total()        5.0%        50.0%    10000
      apply_discount()      40.0%        40.0%    10000  <- HOTSPOT!

Analysis:

  • check_inventory() and apply_discount() are hotspots
  • Called 10,000 times each
  • Together consume 65% of execution time
  • Optimize these functions first

Thread Analysis

Thread Dump Analysis:

"Worker-1" BLOCKED on lock owned by "Worker-2"
  at com.example.Service.processA()
  waiting to lock <0x12345678> (a java.lang.Object)
  which is held by "Worker-2"

"Worker-2" BLOCKED on lock owned by "Worker-1"
  at com.example.Service.processB()
  waiting to lock <0x87654321> (a java.lang.Object)
  which is held by "Worker-1"

Diagnosis: Deadlock detected - Worker-1 and Worker-2 are waiting for each other.

Integrating Analysis into Development Workflow

CI/CD Integration

Static Analysis in Pipeline:

# Example GitHub Actions workflow
name: Code Quality
 
on: [push, pull_request]
 
jobs:
  static-analysis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
 
      - name: Run ESLint
        run: npx eslint src/ --format json > eslint-report.json
 
      - name: Run SonarQube Analysis
        uses: sonarsource/sonarqube-scan-action@master
        with:
          args: >
            -Dsonar.qualitygate.wait=true
 
      - name: Check Security
        run: npx snyk test --severity-threshold=high

Quality Gates

Define Thresholds:

MetricThresholdAction
New bugs0Block merge
Security vulnerabilities0 criticalBlock deploy
Code coverage>80%Warn
Cyclomatic complexity15 max per functionWarn
Technical debt8 hours max newInform

Shift-Left Testing

Catch issues earlier by running analysis:

StageAnalysis Type
IDEReal-time linting, basic static analysis
Pre-commitFormatting, linting, simple security checks
CI BuildFull static analysis, security scanning
Pre-DeployDynamic analysis, performance tests
ProductionMonitoring, APM, error tracking

Tool Selection and Best Practices

Selecting the Right Tools

Criteria for Tool Selection:

FactorConsiderations
Language supportMatches your technology stack
IntegrationWorks with IDE, CI/CD, issue trackers
False positive rateManageable noise level
CustomizationRules can be tuned to your needs
Team adoptionEasy to use, good documentation
CostFits budget (many good free options)

Managing False Positives

Strategies:

# Strategy 1: Inline suppression (use sparingly)
def legacy_function():
    # noqa: C901 - Complex but well-tested legacy code
    ...
 
# Strategy 2: Configuration-based exclusion
# .eslintrc.json
{
  "rules": {
    "no-console": "off"  // Allow in dev environment
  }
}
 
# Strategy 3: Baseline approach
# Compare against known state, only flag new issues

Best Practices Summary

  1. Start with defaults - Use tool defaults before customizing
  2. Fix incrementally - Don't try to fix everything at once
  3. Prioritize security - Always address security issues first
  4. Track trends - Monitor metrics over time, not just snapshots
  5. Balance thoroughness and speed - Not everything needs maximum analysis
  6. Combine approaches - Use both static and dynamic analysis
  7. Educate the team - Ensure everyone understands the tools
  8. Review regularly - Audit rules and thresholds periodically

Test Your Knowledge

Quiz on CTAL-TTA Static and Dynamic Analysis

Your Score: 0/10

Question: What is the fundamental difference between static analysis and dynamic analysis?


Continue Your Certification Journey


Frequently Asked Questions

Frequently Asked Questions (FAQs) / People Also Ask (PAA)

What is the main difference between static and dynamic analysis?

How do I calculate cyclomatic complexity?

What are common static analysis tools I should know for CTAL-TTA?

What memory issues can dynamic analysis detect that static analysis cannot?

How should I integrate static analysis into a CI/CD pipeline?

What should I look for when reviewing code as a Technical Test Analyst?

What is the difference between a profiler and a memory analyzer?

How do I deal with false positives from static analysis tools?