The Nightmare of “Manual Assertions”
Imagine you’ve just finished an API that returns a 150-line JSON object. The usual approach? You manually type out every single line: assert data['user']['address']['city'] == 'Hanoi'. This method is a trap.
Just a slight change in the database structure, and dozens of test cases will break. Manually fixing those assertions is not just frustrating; it’s incredibly time-consuming. I once spent three hours just updating tests after refactoring a data formatting function. What a waste!
Snapshot Testing was created to solve this problem. Instead of comparing every single value, we take a “snapshot” of the ideal result and save it to a file. The next time the test runs, the system automatically compares the new result with this snapshot. If even a comma is out of place, the system alerts you immediately.
Why Should You Switch to Snapshot Testing?
Here is a comparison based on my practical experience when applying it to large-scale projects:
| Criteria | Manual Assertions | Snapshot Testing |
|---|---|---|
| Testing Speed | Very slow (typing every key) | Almost instant |
| Coverage | Usually only checks key fields | 100% data control |
| Maintenance | Painful test code updates | Update with a single command |
| Accuracy | Easy to miss small fields | Absolute precision |
Implementation with Pytest-snapshot
The pytest-snapshot library is a top choice for Python. It’s lightweight, easy to use, and integrates directly into the Pytest workflow.
1. Quick Installation
Open your terminal and run the following command:
pip install pytest-snapshot
2. Testing Complex JSON Data
Suppose you have a get_user_profile function that returns a multi-layered nested dictionary. Instead of writing 20 lines of assertions, use a snapshot:
import pytest
import json
def get_user_profile(user_id):
return {
"id": user_id,
"name": "John Doe",
"metadata": {
"login_count": 10,
"preferences": {"theme": "dark", "lang": "en"}
},
"tags": ["active", "premium"]
}
def test_get_user_profile(snapshot):
user_data = get_user_profile(1)
# Pretty-print JSON to make the snapshot file more readable
snapshot.assert_match(json.dumps(user_data, indent=4), "user_profile_1.json")
When you run pytest for the first time, a snapshots directory is automatically created. If a logic error causes the tags field to disappear later, Pytest will point out exactly which line is missing. Very intuitive!
3. Testing HTML Rendering
Testing HTML interfaces is often annoying due to complex tag structures. Snapshot Testing handles this in just three lines of code:
def test_render_homepage(snapshot, client):
response = client.get("/")
assert response.status_code == 200
snapshot.assert_match(response.data.decode("utf-8"), "homepage.html")
Pro tip: If you need to quickly check Regex patterns before putting them into your code, I often use the Regex Tester. This tool ensures your output string is perfect before you save a permanent snapshot.
Handling Dynamic Data: The Enemy of Snapshots
Dynamic data like created_at or timestamp will cause snapshots to fail repeatedly. Even a one-second difference will break the test. There are two definitive ways to handle this:
- Using Freezegun: Freeze the system time at a specific moment.
- Scrubbing: Overwrite dynamic values with a fixed string before comparison.
Example of simple Scrubbing:
def test_api_with_dynamic_data(snapshot):
raw_data = call_real_api()
# Overwrite timestamp to avoid time mismatch errors
raw_data["created_at"] = "fixed-timestamp"
snapshot.assert_match(json.dumps(raw_data, indent=4), "api_response.json")
Updating Snapshots When Logic Changes
If you intentionally change your logic and want to update all snapshot files, don’t do it manually. Use this command:
pytest --snapshot-update
Important note: Always use git diff to review changes after updating. Don’t accidentally turn a bug into the new “standard result.”
Conclusion
Snapshot Testing isn’t a magic wand, but it significantly reduces your workload. Use it for large datasets, complex structures, or when you need Regression testing. For simple calculation logic like 1 + 1 = 2, traditional assertions are still the best choice. Try applying it to your project today; you’ll find writing tests much more breathable.

