Python firebase testing
This guide explains how to effectively test Firebase Functions in Python using pytest and mocking.
Firebase Functions decorated with @https_fn.on_request() can be challenging to test because they expect specific request formats. This guide provides utilities and patterns to make testing easier.
Call the function directly with mock request objects, bypassing the Firebase Functions framework:
from firebase_test_utils import FirebaseFunctionTester
from main import http_generate_pin
def test_http_generate_pin():
# Create a mock request
request = FirebaseFunctionTester.create_authorized_request(
"http_generate_pin",
query_params={"email": "test@example.com", "uid": "test-uid"}
)
# Mock external dependencies
with patch('main.send_email') as mock_send_email:
mock_send_email.return_value = True
# Call function directly
response = http_generate_pin(request)
# Assert results
assert response.status_code == 200
assert response.data == b"OK"
For more comprehensive testing that includes the HTTP layer:
from flask import Flask
from werkzeug.test import Client
def test_with_flask_client():
app = Flask(__name__)
# Register your function as a route
@app.route('/test_function', methods=['POST'])
def test_route():
from flask import request
return http_generate_pin(request)
client = Client(app)
response = client.post('/test_function?email=test@example.com&uid=test-uid',
headers={'api_key': 'secret'})
assert response.status_code == 200
Always mock external services to avoid side effects:
def test_with_mocked_dependencies(mock_firestore):
with patch('main.send_email') as mock_email, \
patch('main.get_db', return_value=mock_firestore):
# Your test code here
response = http_generate_pin(request)
# Verify mocks were called correctly
mock_email.assert_called_once()
mock_firestore.collection.assert_called_with('users')
Helper class for creating mock requests:
create_authorized_request()- Creates request with valid API keycreate_unauthorized_request()- Creates request with invalid/missing API key
Helper for mocking Firestore operations:
create_mock_firestore()- Creates properly configured mock Firestore clientassert_document_set()- Asserts that documents were set with expected data
def test_successful_operation(mock_firestore):
AppService.instance().database = mock_firestore
request = FirebaseFunctionTester.create_authorized_request(
"your_function",
query_params={"param1": "value1"}
)
with patch('main.external_service') as mock_service:
response = your_function(request)
assert response.status_code == 200
mock_service.assert_called_once()
def test_invalid_input():
request = FirebaseFunctionTester.create_authorized_request(
"your_function",
query_params={"param1": "invalid_value"}
)
response = your_function(request)
assert response.status_code == 400
assert b"Invalid" in response.data
def test_unauthorized_access():
request = FirebaseFunctionTester.create_unauthorized_request(
"your_function",
api_key="wrong_key"
)
response = your_function(request)
assert response.status_code == 403
- Always mock external dependencies - Don’t send real emails, make real API calls, or write to real databases during tests
- Test both success and failure paths - Include tests for invalid inputs, unauthorized access, and error conditions
- Use descriptive test names - Make it clear what scenario each test is covering
- Keep tests isolated - Each test should be independent and not rely on the state from other tests
- Use fixtures for common setup - Create pytest fixtures for commonly used mock objects
- Verify interactions - Assert that mocked services were called with expected parameters
# Run all tests
pytest test/
# Run specific test file
pytest test/main_test.py
# Run with verbose output
pytest -v test/
# Run with coverage
pytest --cov=main test/
Problem: Trying to use Flask’s test client directly with Firebase Functions.
Solution: Use the direct function testing approach shown above.
Problem: The import path in the patch is incorrect.
Solution: Use the path where the function is used, not where it’s defined:
# If main.py imports and uses send_email
with patch('main.send_email'): # Correct
# Not this:
with patch('some_module.send_email'): # Wrong if main.py imports it
Problem: Tests fail because Firebase Functions framework isn’t properly initialized.
Solution: Mock the framework or test functions directly without the decorator.
test/
├── __init__.py
├── conftest.py # Shared fixtures
├── firebase_test_utils.py # Testing utilities
├── main_test.py # Tests for main.py functions
├── test_auth.py # Authentication function tests
├── test_payments.py # Payment function tests
└── README_TESTING.md # This file