Faker: Generate Fake Test Data in Python (2026)
Tests, demos and staging databases need data that looks real, but copying production records leaks personal information and makes results change from run to run. Faker is a popular MIT-licensed Python library that generates realistic names, emails, addresses, dates, bank numbers and much more, in dozens of locales. This tutorial was tested with Faker 40.43.0 on Python 3.13.5, and every output block below is the real output of the scripts shown.
Why Faker in 2026?
- Hundreds of ready-made generators (called providers):
name(),email(),address(),iban(),date_between(),pyfloat(),simple_profile()and many more. - Locale aware:
Faker("de_DE")gives German names and cities, and one instance can hold several locales at once. - Reproducible:
Faker.seed()makes every run produce exactly the same data, so failing tests can be replayed. - Extensible: add your own domain values with
DynamicProvideror aBaseProvidersubclass. - Pure Python with a single small dependency, so it fits any pytest-based testing stack.
Install
pip install Faker
Faker 40.x supports current Python 3 releases. The package name on PyPI is Faker, and you import it as from faker import Faker.
Step 1: Seeds, locales and unique values
Call Faker.seed() before creating data to make it deterministic. Pass a list of locales to get a multi-locale instance and pick one with multi["fr_FR"]. The fake.unique proxy guarantees that no value repeats, which is perfect for primary keys and emails with a UNIQUE constraint.
import sys
import faker
from faker import Faker
print("Python", sys.version.split()[0], "| Faker", faker.VERSION)
Faker.seed(42) # same seed -> same fake data every run
fake = Faker("en_US")
print("name: ", fake.name())
print("email: ", fake.email())
print("address:", fake.address().replace("\n", ", "))
print("date: ", fake.date_between(start_date="-30y", end_date="-18y"))
print("iban: ", fake.iban())
multi = Faker(["de_DE", "fr_FR", "ja_JP"])
multi.seed_instance(7)
for loc in ("de_DE", "fr_FR", "ja_JP"):
print(f"{loc}: {multi[loc].name()} | {multi[loc].city()}")
codes = [fake.unique.random_int(min=1, max=5) for _ in range(5)]
print("unique codes:", sorted(codes))
fake.unique.clear()
Real output (basics)
Running python faker_basics.py printed:
Python 3.13.5 | Faker 40.43.0
name: Allison Hill
email: donaldgarcia@example.net
address: 600 Jeffery Parkways, New Jamesside, MT 29394
date: 2002-03-12
iban: GB92IZAY26542351161559
de_DE: Violetta Flantz | Stadtsteinach
fr_FR: Clémence Delaunay | Saint Valentine
ja_JP: 太田 真綾 | 稲城市
unique codes: [1, 2, 3, 4, 5]
Because the seed is fixed at 42, you will get Allison Hill and the same address every time on the same Faker version. Emails use reserved domains such as example.net, so you never email a real person by accident. The German, French and Japanese lines show native names and city names for each locale. Asking fake.unique.random_int for five values between 1 and 5 returns each number exactly once; a sixth call would raise UniquenessException, and fake.unique.clear() resets the history.
Step 2: Custom providers and a 1,000-row test database
Real applications have their own vocabulary, such as subscription plans or order numbers. DynamicProvider picks from a fixed list, and a BaseProvider subclass can build any value from Faker's random helpers. The script below builds 1,000 customers, loads them into an in-memory SQLite table with a UNIQUE email column, runs a GROUP BY report and proves that the same seed recreates the same data.
import sqlite3
from faker import Faker
from faker.providers import BaseProvider, DynamicProvider
plan_provider = DynamicProvider(
provider_name="plan",
elements=["free", "pro", "team", "enterprise"],
)
class OrderProvider(BaseProvider):
def order_id(self) -> str:
return f"ORD-{self.random_int(100000, 999999)}"
Faker.seed(2026)
fake = Faker("en_US")
fake.add_provider(plan_provider)
fake.add_provider(OrderProvider)
def make_customer(i: int) -> tuple:
return (i, fake.name(), fake.unique.email(), fake.plan(),
fake.country_code(), round(fake.pyfloat(min_value=5, max_value=500, right_digits=2), 2),
fake.order_id(), fake.date_time_this_year().isoformat(timespec="seconds"))
rows = [make_customer(i) for i in range(1, 1001)]
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE customers (id INT, name TEXT, email TEXT UNIQUE, plan TEXT,"
" country TEXT, spend REAL, last_order TEXT, seen_at TEXT)")
db.executemany("INSERT INTO customers VALUES (?,?,?,?,?,?,?,?)", rows)
print("rows inserted:", db.execute("SELECT COUNT(*) FROM customers").fetchone()[0])
print("first row:", rows[0])
for plan, n, avg in db.execute("SELECT plan, COUNT(*), ROUND(AVG(spend), 2) FROM customers"
" GROUP BY plan ORDER BY plan"):
print(f" {plan:<10} {n:>4} customers avg spend {avg}")
print("profile keys:", sorted(fake.simple_profile().keys()))
Faker.seed(2026)
again = Faker("en_US")
again.add_provider(plan_provider)
again.add_provider(OrderProvider)
print("same seed, same first name:", again.name() == rows[0][1])
Real output (dataset)
rows inserted: 1000
first row: (1, 'Jonathan Hunt', 'hensonpatricia@example.net', 'enterprise', 'TO', 285.93, 'ORD-914295', '2026-08-14T22:10:57')
enterprise 243 customers avg spend 241.63
free 252 customers avg spend 256.53
pro 242 customers avg spend 268.28
team 263 customers avg spend 262.57
profile keys: ['address', 'birthdate', 'mail', 'name', 'sex', 'username']
same seed, same first name: True
All 1,000 rows were inserted without a UNIQUE violation thanks to fake.unique.email(). The four plans are spread roughly evenly, which is what a uniform random choice should give, and the average spend sits near the middle of the 5 to 500 range. simple_profile() returns a ready-made dictionary when you need a whole person in one call. Finally, reseeding with 2026 regenerated the exact same first name, which is what makes Faker data safe to use in assertions. The same pattern works for seeding an SQLAlchemy 2.0 ORM model instead of raw SQLite.
Using Faker with pytest
Faker ships a pytest plugin. Once Faker is installed, any test can request the faker fixture, which is seeded to 0 by default for every test. Override the seed with a faker_seed fixture and the locale with faker_locale in your conftest.py:
import pytest
@pytest.fixture(scope="session")
def faker_locale():
return ["en_US"]
@pytest.fixture
def faker_seed():
return 12345
def test_signup_accepts_valid_email(faker):
email = faker.email()
assert "@" in email
Faker gives you realistic examples. If you want a tool that searches for edge cases that break your code, combine it with Hypothesis property-based testing.
Production tips
- Always seed in tests. Random data that changes on every run turns one bug into a flaky test nobody can reproduce.
- Pin the Faker version in test requirements: the generated values for a given seed can change between releases.
- Use
fake.uniquefor columns with UNIQUE constraints, and callfake.unique.clear()between datasets. - Never mix Faker output with real customer data in the same table, and label fake datasets clearly.
- For large volumes, generate rows in batches and use
executemanyor bulk inserts instead of one INSERT per row. - Remember that fake data is not anonymised real data: Faker invents values, it does not mask existing ones.
Conclusion
Faker turns test data into a few lines of Python: seed it, choose your locales, use unique for keys, and add custom providers for your own domain values. With Faker 40.43.0 on Python 3.13 you can fill a test database with 1,000 realistic, reproducible rows in well under a second, without touching real personal data.