7. Testing (pytest)

Write focused tests with fixtures and marks; assert behavior and log output cleanly.

Question: What are some key features of pytest that make it so popular?

Answer: pytest is popular due to its simple assert statements, powerful fixture system for managing test setup and teardown, and rich plugin ecosystem.

Explanation:

  • Fixtures: @pytest.fixture defines reusable setup code (like a database connection) that can be injected into tests.

  • Parametrization: @pytest.mark.parametrize runs the same test with multiple inputs, reducing code duplication.

  • Mocking: pytest integrates well with unittest.mock and plugins like pytest-mock to make mocking dependencies easy for isolating code under test.

import pytest

@pytest.fixture
def db(tmp_path):
    # Setup a temporary test database
    return init_db(tmp_path / "test.db")

@pytest.mark.parametrize("email", ["a@b.com", "x@y.org"])
def test_valid_email(email):
    assert validate(email)

Question: How do you mock dependencies and environment in pytest?

Answer: Use unittest.mock.patch or the monkeypatch fixture to replace attributes, environment variables, or functions during a test.

Explanation: Keep tests deterministic by isolating external effects.

from unittest.mock import patch

def test_uses_time():
    with patch("module.time.time", return_value=0):
        assert compute_timestamp() == 0

def test_env(monkeypatch):
    monkeypatch.setenv("APP_ENV", "test")
    assert get_env() == "test"

Question: What fixture scopes and auto-use options exist?

Answer: Scopes: function, class, module, package, session. Use @pytest.fixture(scope="module", autouse=True) to apply automatically.

Question: How do you use marks and capture logs?

Answer: Use @pytest.mark.slow/xfail to label expectations; use caplog to assert logs.

import pytest

@pytest.mark.slow
def test_logs(caplog):
    with caplog.at_level("INFO"):
        run()
    assert any("started" in m for m in caplog.messages)

Question: How do you measure test coverage?

Answer: Use pytest-cov plugin: pytest --cov=your_package --cov-report=term-missing.

Question: Can you explain the difference between unit, integration, and end-to-end tests?

Answer: Unit tests verify a single, isolated piece of functionality (like a function or a class). Integration tests verify that multiple components work together correctly. End-to-end (E2E) tests verify the entire application flow from the user's perspective.

Explanation:

  • Unit Tests: Fast and numerous. They mock external dependencies (like databases or APIs) to focus solely on the logic of the unit under test.

  • Integration Tests: Slower and fewer than unit tests. They test the interaction between components, for example, checking that your application code can correctly query a real (test) database.

  • E2E Tests: The slowest and fewest. They simulate real user behavior, often by driving a web browser or making real API calls to a deployed environment, to ensure the entire system works as a whole. A healthy test suite has a pyramid shape: many unit tests at the base, fewer integration tests, and even fewer E2E tests at the top.