Test levels

How to write tests at each level — unit, integration, functional, and e2e — with real examples from the codebase.

Table of contents

Unit tests

Mock all external dependencies. No database, no Flask app, no HTTP. Fastest tests.

# splent_feature_auth/tests/unit/test_services.py
from unittest.mock import MagicMock, patch

@patch("splent_io.splent_feature_auth.services.login_user")
def test_authenticate_valid_user(mock_login_user):
    mock_user = MagicMock()
    mock_user.check_password.return_value = True

    repo = MagicMock()
    repo.get_by_email.return_value = mock_user

    service = AuthenticationService(user_repository=repo)
    result = service.login("user@example.com", "1234")

    assert result is True
    mock_login_user.assert_called_once_with(mock_user, remember=True)

Key points: no fixtures needed, mock repositories, patch external calls, test logic in isolation.


Integration tests

Real test database. Verify repositories, queries, and model relationships.

# splent_feature_profile/tests/integration/test_repositories.py
def test_create_and_retrieve_profile(test_client):
    with test_client.application.app_context():
        user = User(email="integration@test.com", active=True)
        user.set_password("pass123")
        db.session.add(user)
        db.session.commit()

        profile = UserProfile(user_id=user.id, name="Test", surname="User")
        db.session.add(profile)
        db.session.commit()

        service = UserProfileService()
        found = service.get_by_user_id(user.id)

        assert found is not None
        assert found.name == "Test"

Key points: use test_client (clean DB per test), wrap in app context, test real queries.


Functional tests

HTTP endpoints through Flask’s test client. Full stack active.

# splent_feature_auth/tests/functional/test_login_flow.py
def test_login_success(test_client, auth_test_user):
    response = test_client.post(
        "/login",
        data={"email": "user1@example.com", "password": "1234"},
        follow_redirects=True,
    )
    assert response.status_code == 200
    assert b"Invalid credentials" not in response.data

def test_notes_requires_login(test_client):
    response = test_client.get("/notes")
    assert response.status_code == 302
    assert "login" in response.headers.get("Location", "").lower()

Key points: use test_client or logged_in_client, assert on status codes, response data, redirects.


E2E tests

Real browser via Selenium. Slow, requires running server.

# splent_feature_notes/tests/e2e/test_browser.py
from splent_framework.environment.host import get_host_for_selenium_testing
from splent_framework.selenium.common import initialize_driver, close_driver

@pytest.fixture()
def browser():
    driver = initialize_driver()
    yield driver
    close_driver(driver)

def test_index_page_loads(browser):
    host = get_host_for_selenium_testing()
    browser.get(f"{host}/notes")
    assert "notes" in browser.page_source.lower()

Run with splent feature:test --e2e.


Test helpers

Auth helpers

from splent_framework.helpers.test_helpers_auth import login, logout

def test_protected_route(test_client):
    login(test_client, "user@example.com", "secret")
    response = test_client.get("/profile/edit")
    assert response.status_code == 200
    logout(test_client)

Extending test_client per file

Override test_client locally to seed file-specific data:

@pytest.fixture(scope="function")
def test_client(test_client):
    with test_client.application.app_context():
        admin = User(email="admin@example.com", active=True)
        admin.set_password("admin1234")
        db.session.add(admin)
        db.session.commit()
    yield test_client

See also


Back to top

splent. Distributed by an LGPL license v3. Contact us: drorganvidez@us.es