Why Mock Data Matters
Layer 4 begins. Real teams build and test an application's logic long before a real data source is ready. We formalize working against fake data on purpose.
Fake data, shaped exactly like the real thing will be.
1. Learning Objective
By the end of this session you will be able to:
- Explain why building against mock data before a real API exists speeds up development
- Write a mock data file that mirrors the shape a future real data source will have
- Explain what a "contract" means in the context of data shape
- Recognise this as a largely concept session, similar to Sessions 08 and 16
- Identify the risk of a mock data shape silently diverging from the real data shape it is meant to mirror
2. Pre-Coding Quiz
Answer these before reading the concept explanation or writing any code. It is fine to get these wrong โ that is the point. You need 4/5 to proceed to the lab.
Why would a team build the Country Explorer's search feature against a hand-written mock_countries.py file instead of waiting for a real API to be ready?
What does it mean for mock data to "mirror the shape" of a future real data source?
name, region, and population keys with string/string/int types, the mock data should use exactly the same keys and types โ even though "Kenya" and 54000000 are made-up placeholder values, not scraped from a real source.What is a "contract" in the sense used by this session?
What risk exists if the mock data's shape silently diverges from what the real data source will actually provide (e.g. mock uses "pop", but the real API returns "population")?
Why is this session mostly concept, with a comparatively small lab?
3. The Concept โ Why Mock Data Matters
Application code depends on the SHAPE (the contract), not on whether the data source is mock or real.
Building against fake data on purpose
Every session so far has technically used mock data โ hand-typed Country instances. This session makes that a deliberate practice: build and test your application's real logic against a fake data source that mirrors what a real one will eventually look like.
# mock_countries.py โ deliberately fake, but shaped like the real thing will be
MOCK_COUNTRIES = [
{"name": "Kenya", "region": "Africa", "population": 54000000},
{"name": "Ghana", "region": "Africa", "population": 31000000},
{"name": "Peru", "region": "Americas", "population": 33000000},
{"name": "Japan", "region": "Asia", "population": 125000000},
{"name": "Norway", "region": "Europe", "population": 5400000},
]
A contract is a shape everyone agrees to honor
The "contract" is simply: every country record has a name (str), a region (str), and a population (int). As long as both the mock data and a future real API honor this same contract, code written against one will keep working against the other.
The independence this buys us
With a mock data source in place, every remaining feature โ search, filtering, the whole CountryExplorer built in Layer 2 and 3 โ can be developed and fully exercised without a real API existing yet, or even being reachable over the network at all.
from mock_countries import MOCK_COUNTRIES
countries = [Country.from_dict(r) for r in MOCK_COUNTRIES]
explorer = CountryExplorer(countries=countries)
print(explorer.total_population) # works completely offline, no network needed
The risk: silent divergence from the real shape
Mock data is only useful if it honestly represents what real data will look like. If the mock and the eventual real source disagree on key names or types, everything built "safely" against the mock will break the moment real data arrives โ an important risk to keep in mind as we build Session 30's formal data contracts.
4. Lab
What you will build
A file called mock_countries.py and a small script that uses it.
Step-by-step instructions
Create mock_countries.py with 10 diverse records
Use a variety of regions and population sizes โ this will matter for later filtering exercises.
# mock_countries.py
MOCK_COUNTRIES = [
{"name": "Kenya", "region": "Africa", "population": 54000000},
{"name": "Ghana", "region": "Africa", "population": 31000000},
{"name": "Nigeria", "region": "Africa", "population": 223000000},
{"name": "Peru", "region": "Americas", "population": 33000000},
{"name": "Canada", "region": "Americas", "population": 38000000},
{"name": "Brazil", "region": "Americas", "population": 216000000},
{"name": "Japan", "region": "Asia", "population": 125000000},
{"name": "India", "region": "Asia", "population": 1428000000},
{"name": "Norway", "region": "Europe", "population": 5400000},
{"name": "Germany", "region": "Europe", "population": 84000000},
]
Create explore_offline.py that imports the mock and builds Country instances
# explore_offline.py
from mock_countries import MOCK_COUNTRIES
class Country:
def __init__(self, name, region, population):
self.name = name
self.region = region
self.population = population
def summary(self):
return f"{self.name} ({self.region}): pop. {self.population:,}"
@classmethod
def from_dict(cls, data):
return cls(**data)
countries = [Country.from_dict(r) for r in MOCK_COUNTRIES]
print(len(countries))
Build a CountryExplorer entirely offline, with total_population and country_count as @property
class CountryExplorer:
def __init__(self, countries):
self.countries = countries
@property
def country_count(self):
return len(self.countries)
@property
def total_population(self):
return sum(c.population for c in self.countries)
explorer = CountryExplorer(countries=countries)
print(explorer.country_count)
print(explorer.total_population)
Print every summary, proving the whole application works with zero network access
for c in explorer.countries:
print(c.summary())
Write a comment describing the contract: what keys and types every record must have
This will become Session 30's formal data contract โ write it in plain English for now.
5. Expected Files Changed
| File | Action | Why |
|---|---|---|
mock_countries.py |
Created | A mock dataset of 10 countries mirroring the eventual real API shape. |
explore_offline.py |
Created | Builds the full explorer entirely from mock data, no network needed. |
docs/sessions/session-27/index.html |
Created | This session document. |
6. Commit Checkpoint
Once the lab is complete and you can explain every line, make this exact commit:
git add mock_countries.py explore_offline.py docs/sessions/session-27/index.html
git commit -m "session-27: build a mock dataset and run the explorer entirely offline"
7. Code Review Checklist
Go through your code line by line and check each item:
8. Post-Coding Quiz
Same 5 questions. Take it again now that you have written and run the code. You need 4/5 to mark this session complete.
Why would a team build the Country Explorer's search feature against a hand-written mock_countries.py file instead of waiting for a real API to be ready?
What does it mean for mock data to "mirror the shape" of a future real data source?
name, region, and population keys with string/string/int types, the mock data should use exactly the same keys and types โ even though "Kenya" and 54000000 are made-up placeholder values, not scraped from a real source.What is a "contract" in the sense used by this session?
What risk exists if the mock data's shape silently diverges from what the real data source will actually provide (e.g. mock uses "pop", but the real API returns "population")?
Why is this session mostly concept, with a comparatively small lab?
9. Reflection Questions
Think through these after the post-quiz. No right answer โ they are for discussion.
- What would break in explore_offline.py if one mock record used "pop" instead of "population" as a key? Where exactly would the failure occur?
- Why is it valuable that explore_offline.py runs with literally zero network access?
- Can you think of a real project (not this one) where you have seen or could imagine mock data being useful before a backend was ready?
- What is the cost of maintaining mock data as the "real" contract evolves over time? How would you keep them in sync?
10. What Breaks If This Knowledge Is Missing?
- Blocked development: Without mock data, building and testing the Country Explorer's logic would be blocked on a real, possibly unfinished or unreliable API being available โ an unnecessary dependency for logic that has nothing to do with networking.
- The data access layer (Session 28): The next session wraps this mock data behind a proper repository interface โ the mock dataset from this session becomes the first "backend" that repository talks to.
- Data contracts (Session 30): The plain-English contract description written in this lab becomes the formal, enforced contract in Session 30, using type hints and dataclasses.
11. What We Learned
Python concept mastered: Deliberately building and testing against mock data that mirrors a real data source's shape, and the concept of a data contract.
Unlocks: The entire application can now be developed and demonstrated completely independent of any real, external data source.
Next session: Session 28 โ Building a Data Access Layer. We wrap this mock data behind a proper data-access layer, separating "how data is fetched" from "what the application does with it."