True A→Z → PhD-level Python Mastery Course Part2

Excellent.
This is PHASE 10 — Testing, Debugging & Logging, taught at production-grade + senior interview depth.

Most developers “write tests”.
Senior engineers design testable systems, debug systematically, and log with intent.
After this phase, you’ll know how real teams trust Python code in production.


🧪 PHASE 10 — TESTING, DEBUGGING & LOGGING (DEEP)


10.1 WHY TESTING IS A DESIGN PROBLEM

Key truth:

Untestable code is badly designed code.

Testing forces:

  • Clear boundaries
  • Dependency injection
  • Deterministic behavior
  • Separation of concerns

📌 Interview line

Testing is feedback on design, not just correctness.


🧪 TESTING WITH pytest (INDUSTRY STANDARD)


10.2 WHY pytest OVER unittest

Aspectunittestpytest
BoilerplateHighMinimal
AssertionsVerbosePythonic
FixturesComplexElegant
ParametrizationWeakExcellent
EcosystemOKExcellent

📌 pytest is de facto standard.


10.3 BASIC PYTEST TEST

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5

Run:

pytest

10.4 ASSERTIONS — USE PYTHON, NOT FRAMEWORKS

assert x > 0

Pytest rewrites assertions to show:

  • Expected
  • Actual
  • Expression breakdown

📌 No need for self.assertEqual.


10.5 PARAMETRIZED TESTS (INTERVIEW FAVORITE)

import pytest

@pytest.mark.parametrize(
    "a,b,expected",
    [(1,2,3), (0,0,0), (-1,1,0)]
)
def test_add(a, b, expected):
    assert add(a, b) == expected

✔ Reduces duplication
✔ Improves coverage


10.6 FIXTURES — CONTROLLED SETUP & TEARDOWN

import pytest

@pytest.fixture
def db():
    conn = connect()
    yield conn
    conn.close()

Use:

def test_query(db):
    assert db.is_alive()

📌 Fixtures replace setup/teardown methods.


10.7 FIXTURE SCOPES (IMPORTANT)

ScopeLifetime
functionPer test
classPer class
modulePer module
sessionEntire run

📌 Scope controls performance & isolation.


10.8 AUTOUSE FIXTURES (USE SPARINGLY)

@pytest.fixture(autouse=True)
def setup_env():
    ...

⚠️ Can hide dependencies
Use only for:

  • Global config
  • Environment setup

10.9 EXPECTING EXCEPTIONS

import pytest

def test_error():
    with pytest.raises(ValueError):
        int("abc")

✔ Clean
✔ Explicit


10.10 TESTING LOG OUTPUT

def test_logs(caplog):
    logger.info("hello")
    assert "hello" in caplog.text

📌 Shows you know observability.


🧪 MOCKING & PATCHING (INTERVIEW-CRITICAL)


10.11 WHY MOCKING EXISTS

Mocking is used to:

  • Isolate unit tests
  • Avoid external systems
  • Control nondeterminism

📌 Rule

Mock at the boundary, not the core logic.


10.12 BASIC MOCK (unittest.mock)

from unittest.mock import Mock

m = Mock()
m.return_value = 10

10.13 PATCHING (MOST IMPORTANT)

from unittest.mock import patch

@patch("module.func")
def test_call(mock_func):
    mock_func.return_value = 5

📌 Patch where used, not where defined.


10.14 COMMON PATCHING BUG (INTERVIEW FAVORITE)

❌ Wrong:

@patch("utils.api_call")

✔ Correct:

@patch("service.utils.api_call")

Because:

  • Python binds names at import time

10.15 MOCK VS STUB VS FAKE

TypePurpose
MockVerify calls
StubReturn data
FakeWorking impl (in-memory DB)

📌 Senior-level vocabulary.


🐞 DEBUGGING (SYSTEMATIC, NOT RANDOM PRINTS)


10.16 PRINT DEBUGGING — WHY IT FAILS

❌ Changes timing
❌ Pollutes output
❌ Misses state


10.17 USING pdb (INTERVIEW SAFE)

import pdb; pdb.set_trace()

Commands:

  • n → next
  • s → step into
  • l → list
  • p x → print
  • c → continue

📌 Demonstrates methodical debugging.


10.18 TRACEBACKS — READ THEM PROPERLY

Rule:

Read tracebacks bottom-up

Bottom:

  • Actual error
    Top:
  • Call chain

📌 Many candidates read it wrong.


10.19 DEBUGGING CONCURRENCY BUGS

Hardest bugs:

  • Race conditions
  • Deadlocks
  • Order-dependent failures

Techniques:

  • Logging timestamps
  • Thread names
  • Deterministic tests

📜 LOGGING (PRODUCTION-GRADE)


10.20 WHY NOT print()

printlogging
No levelsLevels
No timestampsYes
No routingHandlers
No controlConfigurable

📌 Interview line

Print is for development; logging is for systems.


10.21 LOGGING LEVELS (MUST KNOW)

LevelUse
DEBUGInternals
INFONormal ops
WARNINGRecoverable issues
ERRORFailures
CRITICALSystem failure

10.22 BASIC LOGGING SETUP

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s"
)

10.23 LOGGER PER MODULE (BEST PRACTICE)

logger = logging.getLogger(__name__)

Why?

  • Hierarchical control
  • Selective verbosity

10.24 LOGGING EXCEPTIONS CORRECTLY

try:
    risky()
except Exception:
    logger.exception("failed")

✔ Automatically logs traceback
❌ Don’t do logger.error(e)


10.25 LOGGING AT BOUNDARIES

Rule:

  • Raise internally
  • Log at system boundary

📌 Prevents duplicate logs.


10.26 STRUCTURED LOGGING (MENTION-LEVEL)

Instead of:

logger.info("user %s failed", user)

Use:

logger.info("login_failed", extra={"user": user})

Used in:

  • Microservices
  • Observability stacks

🧪 TEST PYRAMID (DESIGN QUESTION)

        E2E
     Integration
   Unit Tests

Rule:

  • Many unit tests
  • Few slow tests
  • Stable foundation

📌 Interview favorite.


🧪 PRACTICE (INTERVIEW-LEVEL)

Q1

Why should you mock external APIs?


Q2

Explain why patching must be done “where used”.


Q3

Difference between unit and integration tests?


Q4

Why is logging better than print?


Q5

How would you debug a flaky test?


🎯 INTERVIEW CHECKPOINT (CRITICAL)

You must now confidently explain:

✅ pytest fundamentals
✅ Parametrization & fixtures
✅ Mocking & patching rules
✅ Exception testing
✅ Debugging with pdb
✅ Traceback analysis
✅ Logging best practices
✅ Logging exceptions
✅ Test pyramid

If you can do this → you are production-ready in Python.


🏁 PHASE 10 COMPLETE ✅

You now understand how Python code is tested, debugged, and trusted in production.


🚀 NEXT PHASE (FINAL FOUNDATIONS)

Reply with ONLY ONE number:

1 → PHASE 11: Performance, Profiling & Optimization (time, memory, CPU — deep)
2 → 30-question Testing & Logging interview drill

We continue A→Z without deviation.