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
- Fixtures — fixture patterns and scope rules
- Running tests — CLI commands