合成數據與虛構數據有何分別?何時應使用?
Synthetic Data vs Fake Data: What's the Difference and When Should You Use Each?
文章區分虛構數據與合成數據:前者可以簡單至只滿足輸入要求,後者則模仿真實數據的結構、分佈或關聯,而不複製實際記錄。作者建議按測試需要選擇數據,簡單測試可用固定測試資料,重視真實組合、變化或大量記錄時則可用合成數據。使用 seed 可重現相同數據;複製生產數據則可能帶來私隱及安全風險。
Developers often use the terms fake data, dummy data, mock data, and synthetic data as if they all mean the same thing.
Sometimes that is harmless.
Sometimes it causes real confusion.
Consider these two records:
{
"name": "Test User",
"email": "test@test.com",
"city": "Test City"
}
and:
{
"name": "Anna Schneider",
"email": "anna.schneider@example.com",
"city": "Hamburg",
"country": "DE",
"postalCode": "20095"
}
Both are not real customer records.
But they serve very different purposes.
The first is obviously placeholder data.
The second is designed to resemble the structure and relationships found in real data.
That difference becomes important when you're testing applications, APIs, imports, forms, analytics pipelines, or databases.
So what exactly is synthetic data, and how is it different from ordinary fake data?
What is fake data?
Fake data is any information created specifically for testing, demonstrations, development, or other non-production purposes.
It can be extremely simple:
John Doe
123 Fake Street
test@example.com
Or even:
foo
bar
123
The goal is usually not realism.
The goal is simply to have something to put into a field.
For example, if you're testing whether a form submits correctly, this might be enough:
{
"firstName": "Test",
"lastName": "User",
"email": "test@example.com"
}
Nothing about that record needs to resemble a real user.
It only needs to satisfy the basic input requirements.
What is synthetic data?
Synthetic data goes a step further.
It is generated data designed to imitate the properties, structure, distributions, or relationships of real data without copying actual records.
For a simple customer profile, that could mean:
{
"firstName": "Anna",
"lastName": "Schneider",
"email": "anna.schneider@example.com",
"country": "DE",
"city": "Hamburg",
"postalCode": "20095"
}
The person is not meant to represent a real customer.
But the record is internally plausible.
The fields relate to each other in a meaningful way.
That makes synthetic data useful for testing systems that behave differently depending on realistic combinations of values.
Fake data can be intentionally unrealistic
Sometimes unrealistic data is exactly what you want.
Suppose you're testing whether a username field accepts a value.
This may be perfectly fine:
test123
You do not need a sophisticated identity generator.
Likewise, if you're testing a calculator:
2 + 2 = 4
there is no benefit in generating realistic financial records.
Simple fake data is often best for:
- unit tests
- simple form tests
- exact business rules
- predictable fixtures
- small examples
- documentation
The simpler the test, the simpler the data should usually be.
Synthetic data is useful when relationships matter
Now imagine a checkout system.
You may need:
name
country
city
postal code
phone number
currency
tax rules
shipping options
If every field is generated independently, you might get:
{
"country": "DE",
"city": "Tokyo",
"postalCode": "SW1A 1AA",
"phone": "+1 212 555 0100"
}
Every individual value looks valid.
Together they make little sense.
If your application uses those relationships, your tests become less useful.
Synthetic data can preserve context.
For example:
{
"country": "DE",
"city": "Hamburg",
"postalCode": "20095",
"phone": "+49 40 12345678"
}
That is much more useful for testing country-aware behavior.
Dummy data is usually simpler
The term dummy data usually refers to placeholder information.
Examples:
John Doe
Company ABC
123456
example@example.com
It is often used for:
- screenshots
- demos
- temporary UI development
- prototypes
- documentation
Dummy data does not necessarily try to reproduce real-world distributions or relationships.
It just fills space.
That makes it useful.
But it is not automatically realistic.
Mock data is slightly different
Mock data often appears in the context of mocked systems.
For example, a frontend may call an API that does not exist yet.
Instead of making a real request:
const response = await fetch("/api/users");
the frontend might receive a mocked response:
{
"id": 42,
"name": "Anna",
"plan": "pro"
}
The important concept is not whether the values are realistic.
The important concept is that the response stands in for another system.
Mocking is about replacing dependencies.
Synthetic data is about generating realistic data.
They can absolutely be used together.
A mocked API can return synthetic data.
Test fixtures are about repeatability
A fixture is data prepared for a test.
For example:
const user = {
id: "user-42",
plan: "pro",
balance: 100
};
Fixtures are often deliberately static.
That is useful because the expected state is obvious.
If a test fails, you know exactly what the input was.
Synthetic data can also be used as a fixture, especially when it is seeded.
For example:
seed = checkout-user-42
The generator can recreate the same synthetic customer every time.
That combines realism with reproducibility.
Realism should match the test
One mistake developers make is assuming that more realistic data is always better.
It isn't.
Suppose you're testing this function:
function addTax(price, rate) {
return price + price * rate;
}
You probably want a test like:
expect(addTax(100, 0.19)).toBe(119);
Generating a realistic customer, address, order, product catalog, and payment method adds nothing.
But if you're testing a full checkout flow, realistic synthetic data becomes much more useful.
A simple rule:
Use the least complicated data that still tests the behavior you care about.
Production data is not test data
There is another category developers sometimes use:
copied production data.
For example:
export 1,000 customers
import into staging
This may create realistic test conditions.
It also creates privacy and security risks.
Production data can contain:
- names
- emails
- addresses
- phone numbers
- account identifiers
- transaction histories
- internal notes
- sensitive metadata
Once copied into development, that information may appear in:
- local databases
- logs
- screenshots
- test exports
- bug reports
- backups
- developer laptops
Synthetic data avoids this problem by generating new records rather than copying actual customers.
Synthetic does not automatically mean anonymous
This distinction is important.
If you take real customer data and simply replace:
John Smith
with:
User 123
the dataset is not necessarily synthetic.
Other attributes may still allow the person to be identified.
For example:
location
age
transaction history
rare job title
specific timestamp
can potentially reveal identity when combined.
Truly synthetic data should be generated rather than merely renaming real individuals.
Format-valid data is another category
Sometimes realism means following a technical format.
For example:
UUID
IBAN
MAC address
IMEI
IP address
phone number
email
A useful test value may need to satisfy structural rules.
For example, this:
DE123
is not very useful for testing a real IBAN validator.
You may instead need an identifier that follows:
country length
BBAN structure
checksum rules
That makes the test data technically realistic.
But an important distinction remains:
Format-valid does not mean real-world verified.
A generated IBAN can satisfy structural checks without representing an existing bank account.
A generated device identifier can match the expected format without representing an actual device.
Synthetic data can preserve distributions
For some applications, individual realistic records are not enough.
Imagine testing analytics.
Production data might contain:
70% free users
20% standard users
10% premium users
A naive generator could produce:
33% free
33% standard
33% premium
That may create misleading results.
A more advanced synthetic dataset could preserve the intended distribution:
free: 70%
standard: 20%
premium: 10%
Likewise, synthetic datasets can model relationships such as:
country → currency
country → phone format
product → price range
subscription → feature usage
age group → behavior
This becomes useful for:
- analytics
- machine learning testing
- load testing
- dashboards
- data pipelines
- BI systems
Synthetic data can still contain edge cases
Realistic does not mean normal.
Good test datasets should include unusual scenarios.
For example:
very long names
empty optional values
Unicode characters
leap-day dates
zero-value orders
duplicate records
minimum values
maximum values
unusual postal formats
These cases often expose more bugs than ordinary records.
A good synthetic-data system should allow realism and deliberate edge cases to coexist.
Example: signup testing
For a simple unit test, this may be enough:
{
"email": "test@example.com"
}
For integration testing:
{
"firstName": "Anna",
"lastName": "Schneider",
"email": "anna.schneider@example.com",
"country": "DE"
}
For edge-case testing:
{
"firstName": "A".repeat(100),
"lastName": "Schneider",
"email": "anna+very-long-alias@example.com",
"country": "DE"
}
Different tests deserve different data strategies.
Example: API testing
Imagine this endpoint:
POST /customers
The API expects:
{
"name": "string",
"email": "string",
"country": "string",
"phone": "string"
}
A basic fixture could be:
{
"name": "Test",
"email": "test@example.com",
"country": "DE",
"phone": "123"
}
That may be enough to test JSON parsing.
But it is not very useful for testing phone validation.
A realistic synthetic fixture could be:
{
"name": "Anna Schneider",
"email": "anna.schneider@example.com",
"country": "DE",
"phone": "+49 40 12345678"
}
Now the same payload exercises more realistic application behavior.
Example: database testing
Suppose you need 100,000 customer rows.
Writing static fixtures is obviously impractical.
A synthetic generator can create:
100,000 records
multiple countries
different subscription plans
varied signup dates
different order histories
That is useful for:
- query testing
- pagination
- indexing
- search
- analytics
- performance testing
For this kind of workload, synthetic data becomes much more valuable than simple dummy records.
Seeds make synthetic data reproducible
Pure generation introduces a problem:
run 1 → dataset A
run 2 → dataset B
run 3 → dataset C
If dataset A exposes a bug, you may want it again.
That is where seeds help.
For example:
seed = regression-42
can produce the same synthetic dataset repeatedly.
Then a CI failure can report:
Generator: customer
Seed: regression-42
A developer can recreate the exact input locally.
This makes synthetic data much more practical for automated testing.
Don't use synthetic data everywhere
Synthetic data is not the answer to every problem.
Use static fixtures when:
- exact values matter
- the test is small
- readability is important
- business rules are simple
Use synthetic data when:
- realistic variation matters
- you need many records
- relationships between fields matter
- you want multiple locales
- you need performance datasets
- production data should not be copied
Use deliberately invalid data when:
- testing validators
- testing error messages
- testing edge cases
- testing defensive code
The best test suite usually combines all three.
A practical hierarchy
I like to think about test data in layers.
Level 1: Placeholder data
foo
bar
123
Useful for very simple tests.
Level 2: Static fixtures
{
"email": "test@example.com",
"plan": "pro"
}
Useful for predictable business logic.
Level 3: Synthetic records
{
"name": "Anna Schneider",
"email": "anna.schneider@example.com",
"country": "DE",
"city": "Hamburg"
}
Useful for realistic application testing.
Level 4: Synthetic datasets
10,000 related records
multiple countries
weighted distributions
edge cases
Useful for analytics, system testing, and performance testing.
The higher the level, the more complexity you introduce.
Only move up when the test actually benefits from it.
Where tools like Genory fit
Developer tools such as Genory focus on generating synthetic test data and structured developer fixtures without requiring real customer records.
For example, you can work with:
- synthetic profiles
- custom datasets
- addresses
- UUIDs
- format-based test identifiers
- validators
- developer utilities
For more complex fixture generation, you can start with the Genory test-data tools.
For programmatic workflows, the current API documentation is available at genory.dev/docs.
The goal is not to replace explicit test fixtures.
The goal is to make realistic test data easier to create when realism actually helps.
A simple rule
When choosing test data, ask:
Does this test care about the exact value?
If yes:
Use an explicit fixture.
If no, ask:
Does realism matter?
If no:
Use simple fake data.
If yes:
Use synthetic data.
And if the test must be reproducible:
Use a seed.
Final thought
Fake data and synthetic data are not enemies.
They solve different problems.
Fake data is often simple, obvious, and predictable.
Synthetic data is useful when your tests benefit from realistic structure, relationships, variation, or scale.
The mistake is not choosing one over the other.
The mistake is using complicated data where simple fixtures would work — or using meaningless placeholders when your application depends on realistic relationships.
Choose the data based on the behavior you're trying to test.
Not based on which generator looks more impressive.
來源:DEV Community · dev.to