Software Testing & QA

Fixtures, Mocks and Test Doubles

35 דק׳

Fixtures

A fixture provides something a test needs, and can clean it up afterwards.

# tests/conftest.py
import pytest
from app import create_app
from app.extensions import db

@pytest.fixture
def app():
    app = create_app("testing")
    with app.app_context():
        db.create_all()
        yield app
        db.session.remove()
        db.drop_all()

@pytest.fixture
def client(app):
    return app.test_client()

Any test that takes a client argument gets a fresh app and database. Fixtures in conftest.py are available to every test in that folder.

Factory fixtures

@pytest.fixture
def make_user(app):
    def _make(email="user@example.com", **kwargs):
        user = User(email=email, username=email.split("@")[0], **kwargs)
        user.set_password("CorrectHorse9!")
        db.session.add(user)
        db.session.commit()
        return user
    return _make

def test_admin_sees_dashboard(client, make_user):
    make_user("admin@example.com", role=UserRole.ADMIN)

Factories create exactly the data each test needs, keeping tests independent.

Mocks: replacing the outside world

Tests must not send real emails or charge real cards. unittest.mock replaces those calls:

from unittest.mock import patch

def test_signup_sends_verification_email(client):
    with patch("app.routes.auth.send_verification_email") as send:
        client.post("/auth/register", data={...})
    send.assert_called_once()

Patch where the name is used, not where it is defined. If auth.py does from app.services.email import send_verification_email, patch app.routes.auth.send_verification_email.

Types of test double

Double What it does
Stub returns canned data (fetch_rates always returns {"eur": 0.9})
Mock records calls so you can assert on them
Fake a working lightweight version (an in-memory database, a fake payment gateway)

Over-mocking makes tests pass while the real system is broken. Mock at the boundaries (network, email, payment, clock) and use real code for everything inside.

Controlling time

from freezegun import freeze_time

@freeze_time("2026-10-20 09:00:00")
def test_streak_continues_next_day():
    ...

Never let a test depend on the real current date; it will fail at midnight or on the 31st of a month.

Temporary files

pytest's built-in tmp_path fixture gives each test a fresh folder:

def test_writes_report(tmp_path):
    out = tmp_path / "report.csv"
    write_report(out)
    assert out.read_text().startswith("id,email")

Check yourself

  • Where should you patch a function that a module imported by name?
  • When is a fake better than a mock?
  • Why freeze time in tests?

ההתקדמות שלכם נשמרת בדפדפן הזה בלבד.

שליחה לבדיקה (10 נקודות)