Regression روی دیتابیس تقریباً خالی سبز است، اما اولین بازپرداخت واقعی ماه شکست میخورد: مبلغ به ریال ذخیره شده ولی ورودی UI تومان بوده، کاربر نام خانوادگی دارای نیمفاصله داشته، Callback درگاه دوبار رسیده و رکورد Ledger از یک روز قبل باقی مانده است. Test caseها اجرا شدند؛ داده تست فقط Happy path ساخته بود.
تولید داده تست یا Test Data Generation یعنی ساخت کنترلشدهٔ ورودی، موجودیت، رابطه، State، توزیع و توالی لازم برای آشکارکردن Risk مشخص. چند نام تصادفی و Email ظاهراً واقعی Dataset قابل اعتماد نمیسازد. داده باید با Requirement و Oracle سازگار، برای Failure قابل بازتولید، از نظر حریم خصوصی قابل دفاع و در زمان تصمیم قابل تحویل باشد.
این راهنما از Test Data Specification و انتخاب تکنیک تا Factory/Faker، Schema-driven، Property-based، Combinatorial، Stateful، Masked subset و Synthetic data را پوشش میدهد؛ سپس Versioning، Validation، CI/CD، انتخاب ابزار و یک مثال کامل پرداخت ایرانی را ارائه میکند.
خلاصهٔ اجرایی تولید داده تست
- قبل از ابزار، Risk، Scenario، Constraint، Relationship، State و حجم لازم را تعریف کنید.
- Example، Fixture، Seed، Generated input، Masked subset و Synthetic dataset یک چیز نیستند.
- برای Regression از Dataset نسخهدار و Seed قابلبازتولید استفاده کنید؛ Randomness خام Evidence ضعیف میسازد.
- Boundary و invalid data را از Requirement بسازید؛ Faker بهتنهایی Domain rule را نمیداند.
- Property-based testing ورودیهای متنوع میسازد و Failure را Shrink میکند؛ جای Oracle را نمیگیرد.
- Combinatorial generation برای Interactionها مفید است؛ Pairwise تضمین پوشش همهٔ Failureها نیست.
- Referential integrity کافی نیست؛ Business invariant و State transition نیز باید معتبر باشد.
- Masked، pseudonymized، encrypted و anonymized مترادف نیستند.
- Synthetic data، بهویژه اگر از داده واقعی آموخته باشد، ذاتاً بدون ریسک حریم خصوصی نیست.
- هر Dataset یک Manifest شامل Spec، generator version، seed، schema، source، privacy class و expiry داشته باشد.
- دادهٔ PR کوچک و سریع، دادهٔ Integration ایزوله و دادهٔ Performance توزیعمحور باشد.
- کیفیت Generator را با Coverage، validity، reproducibility، privacy و زمان تحویل بسنجید، نه تعداد Row.
تولید داده تست دقیقاً چیست؟
| مفهوم | کارکرد | نمونه |
|---|---|---|
| Test data generation | ساخت Values/Entities/Relations/Sequences برای هدف تست | پرداخت با مبلغ مرزی و Callback تکراری |
| Fixture | دادهٔ نسبتاً ثابت برای شروع Test یا Suite | Merchant فعال با Wallet مشخص |
| Seed data | Baseline اولیهٔ سیستم/پایگاه داده | Roleها، Status codeها، Feature config |
| Data factory/builder | API کدنویسی برای ساخت Entity معتبر با Override سناریو | payment(amount=..., status=...) |
| Provisioning | تحویل Dataset به Environment/Consumer | Clone، restore، namespace یا API setup |
| Subsetting | انتخاب بخش مرتبط از Source با حفظ Graph لازم | سفارش و Customer وابسته برای یک بازه |
| Masking/pseudonymization | Transform شناسهها/داده حساس با Risk کنترلشده | Token ثابت برای Customer ID |
| Synthetic data | ساخت رکورد جدید از Rule یا مدل آماری/مولد | توزیع تراکنش با الگوی تعریفشده |
| Test data management | کل چرخهٔ درخواست، تولید، تحویل، نسخه، دسترسی و حذف | Catalog، reservation، refresh و audit |
این مقاله روی «ساخت» تمرکز دارد. معماری Lifecycle، مالکیت و سرویسدهی گستردهتر در راهنمای مدیریت داده تست یا TDM آمده است. مقایسهٔ انتخاب Source نیز در راهنمای داده واقعی، ماسکشده و مصنوعی تکمیل میشود.
دادهٔ شبیه Production همیشه دادهٔ مناسب نیست
برای Boundary test به دادهٔ نادر و هدفمند، برای Property test به فضای ورودی گسترده، برای Performance به Cardinality و Skew نماینده و برای Security به Payload کنترلشده نیاز دارید. شباهت ظاهری نام و آدرس به کاربران واقعی هیچ تضمینی دربارهٔ Risk هدف نمیدهد.
Data generation با Test case generation فرق دارد
Generator ممکن است فقط ورودی بسازد؛ Test هنوز Action، Oracle و Cleanup میخواهد. ابزار Schema-based میتواند Request معتبر یا نامعتبر تولید کند، اما Business correctness پاسخ را باید با Invariant یا Oracle مستقل سنجید.
از Risk به Test Data Specification برسید
پیش از انتخاب Faker یا یک پلتفرم TDM، این قرارداد را بسازید:
dataset_id: refund-ci@12
purpose: بررسی idempotency و ledger consistency بازپرداخت
consumer: refund-contract-and-integration-tests
entities:
customer, merchant, payment, refund, ledger_entry, callback
relationships:
payment.customer_id -> customer.id
refund.payment_id -> payment.id
ledger_entry.reference_id -> refund.id
rules:
payment.amount_irr > 0
0 <= refund.amount_irr <= refundable_amount
idempotency_key unique per merchant/action
duplicate callback must not create duplicate refund
partitions:
refund = zero | partial | full | over-limit
boundaries:
0, 1, refundable-1, refundable, refundable+1
states:
payment=settled; refund=requested|processing|succeeded|failed
locale:
fa-IR, Asia/Tehran, Persian/Latin digits, ZWNJ
volume_profile:
PR=20 scenarios; integration=2k rows; performance=5m rows
privacy_class:
fully synthetic; no production identifiers
reproducibility:
generator=refund-data@3.4.1; seed=14050516
cleanup:
namespace=ci-{run_id}; ttl=6h
سؤالهای کشف نیاز داده
- کدام Risk یا تصمیم با این Dataset سنجیده میشود؟
- Domain معتبر و نامعتبر هر Field چیست؟
- کدام Constraint تکفیلدی و کدام Cross-field است؟
- چه Relationship و Referential graph لازم است؟
- سیستم باید در چه State اولیه و چه Event sequence باشد؟
- آیا Null، Duplicate، Late، Out-of-order یا Concurrent data لازم است؟
- حجم، Cardinality، Distribution، Skew و Temporal shape چیست؟
- کدام Locale، Encoding، Timezone و واحد پول باید پوشش داده شود؟
- Source داده چیست و چه Privacy/Access/Retention policy دارد؟
- Failure چگونه با Seed، Manifest و minimal example بازتولید میشود؟
Schema فقط بخشی از Specification است
amount: integer, minimum: 1 نوع و مرز فنی را میگوید، اما نمیگوید مبلغ باید مضرب ریال باشد، از ماندهٔ قابل بازپرداخت بیشتر نشود یا مجموع Refundها از Payment تجاوز نکند. Test Data Spec باید Schema، Business rule، State و Distribution را کنار هم نگه دارد.
ماتریس انتخاب تکنیک تولید داده
| نیاز | تکنیک غالب | نقطه قوت | محدودیت |
|---|---|---|---|
| Scenario دقیق و قابل فهم | Hand-crafted example/fixture | Intent روشن، review آسان | تنوع و نگهداری محدود |
| Entityهای معتبر پرتکرار | Factory/Builder + Faker | Default معتبر و Override کم | Riskهای پنهان را خودکار کشف نمیکند |
| API/Message schema | Schema-driven | Type/required/range قابل تولید | Business semantics ناقص |
| فضای ورودی و Invariant | Property-based | Generation + shrinking | Property/strategy بد، Signal بد |
| Interaction چند پارامتر | Combinatorial covering array | t-way coverage با Test کمتر | ترتیب و Risk خارج از Model را نمیبیند |
| Workflow و Event order | Model/stateful/sequence | State transition و history | Model/Oracle هزینه دارد |
| Debug یا Golden contract | Versioned golden dataset | ثبات و مقایسه | Stale و بیشبرازش به نمونه |
| ساختار واقعی با حجم کمتر | Masked relational subset | Relationship/shape واقعیتر | Privacy و source coupling |
| Distribution آماری | Rule/statistical/ML synthetic | Scale و کنترل profile | Utility/privacy validation سخت |
| Parser/security robustness | Fuzz/adversarial corpus | ورودی malformed/novel | محیط و Side effect باید مهار شود |
یک Strategy بالغ معمولاً ترکیبی است: چند Example برای Ruleهای بحرانی، Factory برای Setup، Property-based برای Domain، Combinatorial برای Configuration و Dataset توزیعدار برای Performance.
تکنیک اول: Example، Fixture و Golden Dataset
دادهٔ دستی وقتی بد نیست؛ دادهٔ دستی بدون هدف بد است. برای Bug regression، قرارداد مالی یا سناریوی قابل حسابرسی، Example صریح بهترین شروع است:
{
"case": "partial-refund-rounding",
"payment_amount_irr": 100001,
"previous_refunds_irr": 1,
"requested_refund_irr": 100000,
"expected_refundable_after": 0
}
Golden Dataset را نسخهدار کنید
- Rule/Requirement مرجع و expected output را کنار Dataset نگه دارید.
- Schema migration باید compatibility یا migration آن را Test کند.
- تغییر Expected را Review کنید؛ Snapshot update کورکورانه نباشد.
- Dataset کوچک و قابل فهم بماند؛ آرشیو Production را Golden ننامید.
Default معتبر، Override هدفمند
در Data Builder، Entity پیشفرض باید معتبر باشد و هر Test فقط Field مرتبط را Override کند. اگر هر Test دهها Field نامرتبط میسازد، تغییر Schema صدها Test را میشکند و Intent گم میشود.
تکنیک دوم: Rule-based Factory و Faker
Faker برای نام، متن، آدرس، تاریخ و Providerهای locale مفید است؛ اما Business model شما را نمیشناسد. نسخهٔ جاری Faker، locale fa_IR، Seed و Unique proxy دارد. Seed فقط همراه همان ترتیب فراخوانی و نسخهٔ Pinشده بازتولیدپذیر است؛ مستندات هشدار میدهند Datasetهای Provider ممکن است حتی میان Patch versionها تغییر کنند.
from faker import Faker
from random import Random
GENERATOR_VERSION = "refund-data/3.4.1"
SEED = 14050516
fake = Faker("fa_IR")
fake.seed_instance(SEED)
rng = Random(SEED)
def customer(i: int) -> dict:
# شماره کاملاً ساختگی و یکتا در namespace تست
phone = f"0900{1_000_000 + i:07d}"
return {
"id": f"test-customer-{i:06d}",
"name": fake.name(),
"phone": phone,
"locale": "fa-IR",
"is_test": True,
}
def refund(i: int, payment_id: str, refundable: int) -> dict:
amount = rng.choice([1, max(1, refundable - 1), refundable])
return {
"id": f"test-refund-{i:06d}",
"payment_id": payment_id,
"amount_irr": amount,
"idempotency_key": f"ci-{SEED}-{i}",
"status": "requested",
}
پس از Generation، Invariant را Assert کنید
assert row["amount_irr"] > 0
assert row["amount_irr"] <= refundable
assert row["idempotency_key"] not in seen_keys
assert row["payment_id"] in payment_ids
Generator نباید دادهٔ خراب بسازد و Test را به علت Setup fail کند، مگر اینکه هدف همان invalid data باشد. Valid و Invalid generator را جدا نامگذاری کنید.
Unique تصادفی نامحدود نیست
فضای کوچک سرانجام Collision میدهد و Faker نیز پس از تلاشهای متعدد میتواند خطای Uniqueness بدهد. برای کلید فنی از Counter/UUID namespaceدار یا sequence قطعی استفاده کنید؛ Random name را هویت Entity قرار ندهید.
محدودیت locale را Test کنید
وجود Provider فارسی به معنی صحت همهٔ الگوهای پستی، موبایل، کد ملی یا توزیع واقعی نیست. خروجی Provider را Sample و Contract test کنید. برای شناسههای دارای Check digit، Generator دامنهای بنویسید؛ از کد هویتی واقعی یا «عدد تصادفی شبیه کد ملی» استفاده نکنید.
تکنیک سوم: تقسیمبندی و مقادیر مرزی
Requirement را به Classهای همرفتار و Boundaryها تبدیل کنید. برای مبلغ قابل بازپرداخت ۱ تا ۵۰۰٬۰۰۰٬۰۰۰ ریال، دادهٔ ساده میتواند این مجموعه را داشته باشد:
| Class | مقادیر نمونه | انتظار |
|---|---|---|
| زیر حد پایین | -۱، ۰ | Reject؛ بدون Side effect |
| حد پایین | 1 | Accept در صورت سایر شروط |
| داخل دامنه | ۲، مقدار میانی، max-۱ | Accept |
| حد بالا | 500000000 | Accept اگر refundable کافی است |
| بالای حد | 500000001 | Reject |
| نوع/قالب نامعتبر | null، رشته، اعشار | طبق API contract |
سپس Constraint دوم را اضافه کنید: requested ≤ refundable. مرز فنی ۵۰۰ میلیون کافی نیست؛ اگر ماندهٔ Payment صد هزار ریال است، ۱۰۰۰۰۱ نیز Boundary کسبوکار است. روش کامل در راهنمای تقسیمبندی همارزی و تحلیل مقدار مرزی آمده است.
تکنیک چهارم: Schema-driven Generation
JSON Schema و OpenAPI میتوانند Type، Required، Range، Length، Pattern، Array size و Composition را به Generator بدهند. برای APIهای بزرگ، این کار Drift میان Spec و Data factory را کاهش میدهد.
type: object
required: [payment_id, amount_irr, idempotency_key]
properties:
payment_id:
type: string
pattern: "^test-pay-[0-9]{6}$"
amount_irr:
type: integer
minimum: 1
maximum: 500000000
idempotency_key:
type: string
minLength: 12
maxLength: 64
additionalProperties: false
Positive و Negative generation را جدا کنید
- Positive: نمونههایی که تمام Constraintهای Schema را پاس میکنند.
- Negative single-fault: هر بار یک Constraint را نقض میکند تا علت روشن بماند.
- Negative interaction: چند نقض هدفمند برای Parser/validation order.
- Business-invalid: Schema-valid اما Rule دامنه را نقض میکند، مثل Refund بیش از مانده.
Schemathesis از OpenAPI/GraphQL برای تولید Property-based input و گزارش نمونهٔ بازتولیدپذیر استفاده میکند. این ابزار نمونهٔ یک دسته است؛ پیش از انتخاب، Authentication، Stateful flow، Side effect و Oracleهای موردنیاز خود را در PoC بسنجید. برای اصول Assertionهای API به راهنمای تست API مراجعه کنید.
تکنیک پنجم: Property-Based Testing و Shrinking
در Example-based testing میپرسید «این سه ورودی درستاند؟»؛ در Property-based testing فضای ورودی را با Strategy توصیف و Invariant را روی نمونههای متعدد بررسی میکنید. هنگام Failure، ابزارهایی مانند Hypothesis تلاش میکنند ورودی را به مثال کوچکتر و قابل فهمتر Shrink کنند.
from hypothesis import given, strategies as st
amounts = st.integers(min_value=1, max_value=500_000_000)
@given(paid=amounts, already_refunded=amounts, requested=amounts)
def test_refund_never_exceeds_remaining(paid, already_refunded, requested):
remaining = max(0, paid - already_refunded)
result = calculate_refund(paid, already_refunded, requested)
assert result.approved_amount >= 0
assert result.approved_amount <= remaining
Strategy باید Domain را درست مدل کند
مثال بالا بسیاری از ترکیبهای already_refunded > paid میسازد. اگر این State در سیستم ناممکن است، Strategy وابسته بسازید؛ اگر هدف robustness است، آن را عمداً در invalid strategy نگه دارید. Filterهای سنگین نیز Generation را کند میکنند؛ Relationship را هنگام ساخت داده اعمال کنید.
Shrinking جای Debug را نمیگیرد
Minimal failing example نشان میدهد چه ورودی کوچکی Failure را حفظ میکند، نه اینکه Root cause چیست. Seed/example database، Generator version، Build و Environment را همراه Failure ثبت کنید. Non-deterministic SUT میتواند بازپخش را بیاعتبار کند.
Property بدون Oracle فقط ترافیک میسازد
Propertyهای مناسب میتوانند Invariant، Metamorphic relation، Round-trip، monotonicity، idempotency یا عدم Side effect باشند. «پاسخ ۵۰۰ نباشد» حداقل robustness است، نه اثبات صحت کسبوکار.
تکنیک ششم: Combinatorial و t-way
فرض کنید PSP دو مقدار، نوع Refund سه مقدار، Callback دو مقدار و Locale دو مقدار دارد؛ Full Cartesian product برابر ۲۴ ترکیب است و با افزودن Browser/Role/Flag سریع رشد میکند. Covering array مجموعهای کوچکتر میسازد که همهٔ Interactionهای t-way انتخابشده را پوشش دهد.
| پارامتر | مقادیر |
|---|---|
| PSP | A، B |
| Refund | partial، full، over-limit |
| Callback | single، duplicate |
| Digits | Latin، Persian |
| Feature flag | old، new |
NIST ACTS میتواند Test set با t-way coverage، Constraint و variable strength تولید کند. برای مسیر مالی میتوانید strength بالاتر و برای ترکیبهای کمریسک pairwise انتخاب کنید.
Constraint نامعتبر را وارد Model کنید
اگر PSP-B از Refund کامل روی نسخهٔ قدیمی پشتیبانی نمیکند، Generator نباید آن ترکیب را بیصدا حذف یا بسازد؛ Constraint باید در Model ثبت و Coverage denominator با آن محاسبه شود.
Pairwise یک تضمین مطلق نیست
Pairwise همهٔ زوجهای مقدار را میپوشاند، نه تمام Sequenceها، Boundaryها یا Interactionهای سهتایی و بیشتر. Risk و Failure history باید strength و پارامترهای مدل را تعیین کند.
تکنیک هفتم: State، Sequence و Event Data
بسیاری از باگها در Row منفرد نیستند؛ در تاریخچهاند:
- Payment settled → Refund requested → Callback duplicate؛
- Refund pending → cancel → late success callback؛
- Schema old event → new consumer؛
- Retry با همان idempotency key پس از timeout؛
- دو Worker همزمان روی یک Payment؛
- Event out-of-order یا تحویل دوباره.
Model-based/Stateful generator State معتبر، Transition مجاز و Action sequence را تولید میکند. Oracle میتواند State machine، Ledger invariant و Side-effect count باشد. برای پایگاه داده، Transaction و Concurrency باید مستقل بررسی شود؛ راهنمای تست پایگاه داده الگوهای Constraint، Migration و Isolation را پوشش میدهد.
Event envelope را هم تولید کنید
فقط Payload دامنه کافی نیست. Message ID، timestamp، schema version، partition key، trace context، retry count و ordering metadata بخشی از داده تستاند. Duplicate باید دقیقاً همان Event ID یا Payload تعریفشده را داشته باشد؛ «دو رکورد شبیه» ممکن است مسیر دیگری را Test کند.
دادهٔ مشتق از Production؛ Masking و Subsetting
گاهی ساختار، توزیع یا Graph واقعی برای Debug و Migration مهم است. جریان امنتر چنین است:
- Source و Data owner را مشخص و استفاده را مجاز کنید.
- Direct identifier، quasi-identifier، sensitive field و secret را کشف کنید.
- Subset را از Entity ریشه با Closure روابط لازم استخراج کنید.
- Transformهای سازگار و referentially consistent اعمال کنید.
- Utility، Domain invariant و Distribution هدف را اعتبارسنجی کنید.
- Re-identification/privacy risk را مستقل ارزیابی کنید.
- Dataset را در مسیر محدود، رمزشده و Auditشده تحویل دهید.
- TTL، revocation، refresh و deletion evidence داشته باشید.
Masking، Encryption و Anonymization مترادف نیستند
- Encryption: با Key قابل بازگشت است و بیشتر کنترل محرمانگی است.
- Tokenization/pseudonymization: Linkability را ممکن است حفظ کند؛ داده هنوز میتواند شخصی/حساس تلقی شود.
- Masking: خانوادهای از Transformهاست؛ کیفیت آن به Context و Attack model بستگی دارد.
- Anonymization/de-identification: ادعایی دربارهٔ کاهش Risk شناسایی است، نه نام یک تابع ساده.
Hash بدون Salt روی شماره موبایل فضای کوچک قابل حدس دارد؛ Shuffle ستون نیز با quasi-identifierهای دیگر قابل Link است. NIST SP ۸۰۰-۱۸۸ تأکید میکند Privacy risk ممکن است پس از de-identification باقی بماند و انتشار Datasetهای مرتبط در آینده Risk را تغییر دهد.
Synthetic data ذاتاً خصوصی نیست
دادهٔ Rule-based که هرگز Production را نمیبیند Surface متفاوتی دارد؛ اما مدل مولد آموزشدیده روی داده واقعی ممکن است رکورد، outlier یا الگوهای قابل انتساب را حفظ کند. Utility و Privacy را جدا بسنجید: similarity آماری بالا میتواند Risk leakage را نیز بالا ببرد. عبارت «کاملاً امن» بدون Threat model، آزمایش و governance قابل دفاع نیست.
دادهٔ Performance؛ حجم تنها کافی نیست
پنج میلیون Row یکنواخت معمولاً نمایندهٔ Production نیست. Profile باید Dimensionهای مرتبط با Query و Workload را داشته باشد:
- Cardinality کلیدها و نسبت Active/Inactive؛
- Skew و Hot merchant/hot partition؛
- توزیع مبلغ، Status، Region و Channel؛
- Temporal shape، burst، seasonality و aged data؛
- Row/document size و payload distribution؛
- عمق Relation و fan-out؛
- Index selectivity، NULL/duplicate rate و fragmentation؛
- Event ordering، lag و retry rate؛
- Growth/churn، archive و retention.
Dataset Performance را از Workload model جدا نکنید: اگر Query فقط روی آخرین ۲۴ ساعت است، میلیونها رکورد قدیمی ممکن است plan واقعی را نسازد. طرح Test و معیارها در راهنمای تست Performance آمده است.
Generation نباید خود Test را آلوده کند
Data load ممکن است cache را گرم، index را متورم یا log را پر کند. پس از Load، stabilization، statistics refresh و health check تعریف کنید. زمان Generation را از Measurement window جدا نگه دارید.
Negative، Fuzz و دادهٔ امنیتی
Invalid data باید دستهبندی داشته باشد:
- Type/format/length violation؛
- Encoding و Unicode نامعمول؛
- Oversized/deeply nested payload؛
- Duplicate، replay و out-of-order؛
- Unauthorized cross-tenant reference؛
- Injection payload برای Parser/Interpreter؛
- Malicious file metadata/content؛
- Resource-exhaustion shape.
Payload مخرب را فقط در محیط مجاز، با Rule of Engagement، containment و cleanup اجرا کنید. دادهٔ Security ممکن است در Log، Report یا Message bus منتشر شود؛ Label و دسترسی آن را محدود کنید. راهبرد کامل در راهنمای تست امنیت آمده است.
معماری Dataset قابل نگهداری
Baseline + Scenario patch
یک Baseline کوچک از Reference data و Entityهای پایدار بسازید؛ هر Scenario فقط Patch لازم را اضافه کند. Clone بزرگ مشترک باعث coupling، contention و Failure وابسته به ترتیب میشود.
Namespace و مالکیت
شناسهها را با run_id یا tenant آزمایشی Namespaceدار کنید. هر Producer باید Consumer، owner، TTL و cleanup policy داشته باشد. Environment shared بدون namespace، Testها را به هم آلوده میکند؛ الگوی Lease/TTL در راهنمای مدیریت محیط تست شرح داده شده است.
Setup idempotent و Cleanup قابل مشاهده
- اجرای دوبارهٔ Setup یا همان State را میسازد یا Conflict روشن میدهد.
- Partial failure قابل resume/rollback است.
- Cleanup در finally/teardown اجرا و Failure آن Alert میشود.
- Side effect بیرونی مانند پیامک، Email یا PSP به sink آزمایشی میرود.
- Dataset abandoned با TTL پاک میشود.
Manifest هر Dataset
dataset_id / version
spec_hash / schema_version
generator_name / exact_version / source_commit
seed / generation_time / generated_by
source_class: rule | masked-subset | statistical | ml-synthetic
source_snapshot_id (if applicable)
row_counts / entity_counts / checksums
locale / timezone / encoding / currency_unit
privacy_class / transformations / assessment_id
environment_compatibility
owner / consumers / access_policy
created_at / expires_at / cleanup_status
Seed بدون Generator version و Spec hash کافی نیست. نسخهٔ Library، ترتیب فراخوانی و Dataset provider میتواند خروجی را تغییر دهد.
Validation؛ Generator را هم Test کنید
| Gate | سؤال | نمونهٔ Check |
|---|---|---|
| Schema | Type/required/range درست است؟ | JSON Schema/DB constraint validation |
| Relationship | Orphan یا broken reference داریم؟ | Foreign-key/graph closure |
| Business invariant | دادهٔ valid واقعاً ممکن است؟ | refund total ≤ payment |
| Coverage | Partition/boundary/state/t-way هدف پوشش دارد؟ | coverage manifest |
| Distribution | Profile هدف حفظ شده؟ | quantile/cardinality/skew checks |
| Uniqueness | کلید در Scope لازم یکتاست؟ | duplicate query |
| Reproducibility | Manifest یکسان نتیجهٔ یکسان میدهد؟ | checksum on canonical output |
| Privacy | Identifier/secret/leakage باقی مانده؟ | discovery + risk assessment |
| Utility | Dataset سؤال تست را جواب میدهد؟ | representative scenario checks |
| Operational | زمان/حجم/cleanup مناسب است؟ | SLA، TTL و orphan-resource scan |
Realism را Operational تعریف کنید
«واقعی به نظر میرسد» معیار نیست. Realism را به ویژگی هدف تبدیل کنید: توزیع مبلغ، تعداد Item، عمق Relation، درصد Null، طول متن، State mix یا event lag. ممکن است Dataset برای Functional testing مناسب و برای Capacity planning کاملاً نامناسب باشد.
Canonical output برای Checksum
اگر ترتیب Row یا timestamp تصادفی است، checksum مستقیم هر بار فرق میکند. Fieldهای غیرمعنادار را Normalize، Rowها را با Key پایدار sort و format را canonical کنید؛ در غیر این صورت Test بازتولیدپذیری خودش flaky میشود.
طراحی داده برای فارسی و ایران
| بعد | دادههای لازم | Risk |
|---|---|---|
| Unicode | ی/ی، ک/ک، ZWNJ، فاصله، ترکیب RTL/LTR | جستوجو، equality و نمایش |
| Digits | ۰۱۲، ۰۱۲ و ۰۱۲ | parse/validation و normalization |
| Money | IRR/تومان، عدد بزرگ، separator | ضریب ده و rounding |
| Date/time | شمسی/میلادی، ISO، Asia/Tehran، midnight | cutoff، expiry و گزارش |
| Names | کوتاه/بلند، چندبخشی، نیمفاصله و مشابه | sort/search/truncation |
| Address | چندخطی، پلاک/واحد، طول زیاد | UI، label و delivery integration |
| Mobile | قالب valid/invalid کاملاً ساختگی | OTP و اشتباه در ارسال واقعی |
| Identity | Generator دارای check digit در namespace تست | استفاده تصادفی از شناسهٔ واقعی |
برای SMS و Email، sink یا domain رزروشدهٔ تست داشته باشید. ساخت عددی که «احتمالاً واقعی نیست» سیاست ایمنی نیست. Prefix آزمایشی، allowlist و block خروجی واقعی را در چند لایه اعمال کنید.
مثال عملی: Generator دادهٔ پرداخت و بازپرداخت
۱. Risk map
- Refund بیش از مانده؛
- دو بار اعمالشدن Callback؛
- اختلاف ریال و تومان؛
- State ناسازگار Payment/Refund/Ledger؛
- PSP/Feature flag interaction؛
- متن و رقم فارسی؛
- Event دیررس یا out-of-order؛
- Tenant isolation.
۲. لایههای Generator
| لایه | خروجی |
|---|---|
| Reference seed | Status، currency=IRR، PSP config، roles |
| Factory | Customer/Merchant/Payment معتبر |
| Scenario builder | Refund partial/full/over-limit و Ledger |
| Boundary strategy | ۰، ۱، remaining-۱، remaining، remaining+۱ |
| Combinatorial | PSP × callback × digits × flag |
| Sequence | request→timeout→retry→duplicate callback |
| Performance profile | hot merchant، aged data، status skew |
| Negative corpus | cross-tenant ID، malformed amount، oversized key |
۳. Oracleهای مستقل
- مجموع Refund موفق از Payment بیشتر نیست.
- هر idempotency key حداکثر یک Business effect دارد.
- Debit/Credit Ledger برای Refund تراز است.
- Callback تکراری Result قبلی را برمیگرداند و Entry تازه نمیسازد.
- Tenant دیگر هیچ Payment قابل استفادهای ندارد.
- Amount در Storage و API واحد صریح IRR دارد.
۴. Manifest اجرای CI
dataset_id: refund-ci@12
run_id: ci-88421
generator: refund-data@3.4.1
commit: 9f31c2a
seed: 14050516
schema: payment-db@214
rows:
customer: 12
merchant: 4
payment: 48
refund: 76
ledger_entry: 152
coverage:
partitions: 4/4
boundaries: 5/5
pairwise_model: 100% valid pairs
privacy: fully-rule-generated/no-production-source
namespace: ci-88421
expires_at: 1405-05-16T15:00:00+03:30
۵. Failure قابل بازپخش
Property test روی remaining=1 و رقم فارسی Failure میدهد. Report باید minimal example، Seed، Generator version، Config و Build را ثبت کند. Example کوچک به Regression corpus افزوده میشود؛ Generator اصلاحشده نباید History قبلی را پاک کند.
تحویل داده در CI/CD
| Lane | Dataset | زمان/چرخه |
|---|---|---|
| Unit/component | In-memory factory/property input | هر commit؛ میلیثانیه/ثانیه |
| PR integration | Baseline کوچک + scenario patch، namespaceدار | هر PR؛ چند دقیقه |
| Contract/API | Schema/property generated + explicit examples | PR/Nightly |
| Nightly system | ماتریس گسترده، sequence و locale | شبانه؛ TTL |
| Performance | Versioned distribution profile در محیط اختصاصی | On-demand/window |
| Migration | Golden + masked/synthetic representative subset | قبل از release/migration |
Cache یا Generate on demand؟
- Generate: Isolation و Freshness بهتر؛ هزینهٔ زمان و compute.
- Cached snapshot: سریعتر؛ Risk stale/schema mismatch و contention.
- Hybrid: Base snapshot نسخهدار + per-run patch/namespace.
Cache key باید Schema، Generator، Spec و profile را شامل شود. «latest-test-data» بهاندازهٔ «latest-build» مبهم است.
Fail fast روی Data gate
قبل از Suite، checksum/Schema/Invariant/Privacy marker و Environment compatibility را بررسی کنید. اگر Provisioning ناقص است، نتیجه را Data/Environment failure ثبت کنید، نه صدها Product failure.
انتخاب ابزار تولید داده تست
بهجای فهرست محبوبیت، ابتدا دستهٔ مسئله را انتخاب کنید:
| دسته | نمونهها | مناسب برای |
|---|---|---|
| Fake data library | Faker، Mimesis و مشابه | Value/locale و Factory کدنویسی |
| Property-based library | Hypothesis، fast-check، jqwik | Strategy، shrinking و stateful input |
| Schema/API generator | Schemathesis و ابزارهای OpenAPI/GraphQL | Request/response schema exploration |
| Combinatorial | NIST ACTS و covering-array tools | پارامتر/Configuration interaction |
| DB/script factory | SQL، migration seed، language builders | Relation و domain-specific setup |
| Mask/subset platform | Commercial/open tooling | Production-derived graph و governance |
| Synthetic platform | Rule/statistical/ML systems | Scale/distribution با utility/privacy evaluation |
| Environment/data service | TDM catalog، snapshot/clone API | Self-service provisioning و lifecycle |
این نامها مثال خانوادهاند، نه رتبهبندی یا توصیهٔ خرید. Capability، license و رفتار نسخهٔ جاری را در مستندات رسمی و PoC بررسی کنید.
Must-have، Preference و Disqualifier
- Must-have: Constraint/relationship، deterministic seed/version، export، CI API، access control و cleanup.
- Preference: locale، shrinking، stateful model، distribution profiling، visual authoring یا self-service.
- Disqualifier: نیاز اجباری به Upload داده حساس خارج سازمان، نبود audit/export، عدم pin نسخه، Lock-in بدون recovery، یا عدم کارکرد در شبکهٔ شما.
PoC قابل اندازهگیری
یک Dataset واقعی ولی کمریسک انتخاب و این سناریوها را اجرا کنید:
- سه Entity مرتبط با Cross-field rule بسازید.
- Boundary و invalid single-fault تولید کنید.
- با یک Seed دو بار خروجی canonical یکسان بگیرید.
- Schema را تغییر دهید و هزینهٔ نگهداری را بسنجید.
- صد هزار/یک میلیون Row با profile هدف بسازید.
- Failure را به minimal/replayable example برسانید.
- PII/secret scan و access audit اجرا کنید.
- Pipeline قطع، retry و cleanup failure را شبیهسازی کنید.
- در شبکه و محدودیت پرداخت/دسترسی ایران، install/update/export را امتحان کنید.
| معیار | وزن نمونه | Evidence |
|---|---|---|
| Domain constraint/relations | 20 | درصد invariant pass و زمان مدلسازی |
| Reproducibility/debug | 15 | replay و minimal failure |
| Privacy/security | 20 | architecture، scan و audit trail |
| CI/operations | 15 | API، idempotency، TTL و recovery |
| Scale/profile | 10 | throughput و distribution error |
| Maintainability | 10 | schema-change exercise |
| Iran/offline viability | 10 | install، mirror، export و support path |
وزنها باید از Risk شما بیایند. Score بالا جای Security gate یا Disqualifier را نمیگیرد.
متریکهای برنامه تولید داده
- Time to usable dataset: از درخواست تا Dataset validated؛ با Waiting جدا.
- Provision success rate: تحویلهای کامل ÷ تلاشها؛ علت Failure تفکیک شود.
- Invariant failure rate: Datasetهای ردشده در Gate و Rule مربوط.
- Coverage attainment: partition/boundary/state/t-way هدف که داده دارد.
- Reproduction rate: Failureهایی که با Manifest قابل بازپخشاند.
- Data-related test failure: Failureهای ناشی از Setup/Data، با trend و owner.
- Staleness: فاصلهٔ Dataset spec/schema از نسخهٔ هدف.
- Privacy findings: direct/quasi/secret یافتهشده و زمان رفع.
- Orphan/cleanup: Namespace یا Resource منقضیِ پاکنشده.
- Generator change failure: Regressionهای ناشی از تغییر Generator/Provider.
«تعداد رکورد تولیدشده» فقط throughput است و بهتنهایی Utility یا Coverage را نمیسنجد. همچنین درصد استفادهٔ Synthetic هدف سازمانی مناسبی نیست؛ تکنیک باید تابع Risk باشد.
برنامهٔ ۳۰ روزه پیادهسازی
هفتهٔ اول: Inventory و Contract
- Dataset/fixture/scriptهای موجود، owner و Consumer را فهرست کنید.
- یک Flow پرریسک را انتخاب و Test Data Specification بنویسید.
- Privacy class، naming، namespace و Manifest را استاندارد کنید.
هفتهٔ دوم: Factory و Gate
- Base factory با default معتبر و scenario override بسازید.
- Schema، relation و business invariant gate اضافه کنید.
- نسخهٔ Generator و Seed را در Result ثبت کنید.
هفتهٔ سوم: Coverage و CI
- Boundary/partition و یک Property-based test اضافه کنید.
- یک Combinatorial model کوچک برای Configها بسازید.
- PR dataset را namespaceدار و cleanup/TTL را observable کنید.
هفتهٔ چهارم: Privacy، Scale و تصمیم
- PII/secret scan و Threat model Dataset را اجرا کنید.
- یک profile کوچک Performance را با distribution gate Pilot کنید.
- PoC ابزار را با Must-have/Disqualifier و schema-change exercise جمعبندی کنید.
- Metric baseline و backlog بهبود ماه بعد را ثبت کنید.
خطاهای رایج در تولید داده تست
- Faker مساوی Realism: نام واقعینما Rule، Distribution یا State درست نمیسازد.
- Seed بدون نسخه: Update کتابخانه خروجی را تغییر میدهد.
- Random everywhere: Failure قابل بازپخش نیست و Intent گم میشود.
- فقط Valid data: Boundary، invalid و robustness پوشش نمیگیرد.
- فقط Schema: Cross-field، State و Business invariant جا میماند.
- Foreign key مساوی کیفیت: Entity ممکن است relationally valid ولی تجاری ناممکن باشد.
- Full Cartesian matrix: زمان منفجر میشود؛ Risk/t-way مدل نشده است.
- Pairwise مساوی پوشش کامل: Sequence و interactionهای قویتر پنهان میماند.
- Masked مساوی Anonymous: Re-identification و linkage سنجیده نمیشود.
- Synthetic مساوی خصوصی: Source/model leakage نادیده گرفته میشود.
- Production clone مشترک: PII، contention، drift و cleanup مشکل میسازد.
- حجم بدون توزیع: Performance result به Production تعمیمپذیر نیست.
- Cleanup ساکت: دادهٔ Run قبلی Test بعدی را آلوده میکند.
- Tool-first: پلتفرم خریده میشود، اما Data contract و owner وجود ندارد.
چکلیست نهایی Test Data Generation
Specification
- Risk، Consumer و Decision روشن است.
- Entity/Relationship/State/Sequence مدل شده است.
- Valid/invalid partition و Boundary تعریف شده است.
- Volume/Cardinality/Distribution/locale هدف دارد.
- Privacy class، Source و Retention مشخص است.
Generator
- Default valid و Scenario override کمینه است.
- Generator/spec/library exact version ثبت میشود.
- Seed یا minimal example برای Replay وجود دارد.
- کلیدها namespaceدار و Collision policy روشن است.
- Schema و Business invariant هر دو اعمال میشوند.
- Negative/fuzz data از valid dataset جداست.
Validation و Delivery
- Schema، relation، invariant و coverage gate سبز است.
- Distribution/utility متناسب با Test هدف سنجیده شده است.
- PII/secret/privacy assessment انجام شده است.
- Manifest، checksum و environment compatibility ثبت است.
- Provisioning idempotent، namespaceدار و observable است.
- TTL، Cleanup و orphan detection وجود دارد.
- Failure به Data/Product/Environment درست طبقهبندی میشود.
جمعبندی
ابزار تولید داده تست زمانی ارزش دارد که یک Specification آزمونپذیر را به Dataset قابل اعتماد تبدیل کند. Example برای Intent، Factory/Faker برای ساخت Entity، Schema-driven برای Constraint فنی، Property-based برای فضای ورودی، Combinatorial برای Interaction، State model برای توالی و Dataset توزیعدار برای Performance هرکدام مسئلهای متفاوت حل میکنند.
از یک Flow پرریسک شروع کنید: Entity و Rule را بنویسید، Boundary و State را استخراج کنید، Generator نسخهدار بسازید، Manifest و Gate اضافه کنید و Failure را با Seed/minimal example بازپخش کنید. سپس فقط در جایی که نیاز روشن دارید سراغ masked subset، ML synthetic یا پلتفرم سازمانی بروید.
سؤالات متداول
بهترین ابزار تولید داده تست کدام است؟
یک برندهٔ عمومی وجود ندارد. Faker برای Valueهای ساختگی، Hypothesis/fast-check برای Property-based، ابزار Schema برای API، ACTS برای t-way و پلتفرم TDM برای Mask/subset/provisioning مناسباند. Must-have، Privacy، Scale، CI و محدودیت شبکه/هزینه را با PoC واقعی بسنجید.
Faker چگونه داده تست فارسی تولید میکند؟
Faker از locale fa_IR برای برخی Providerها مانند نام و آدرس پشتیبانی میکند. Generator را با locale و Seed بسازید و نسخه را Pin کنید؛ سپس خروجی را با Ruleهای دامنه، قالب موبایل، ریال/تومان، Unicode و Check digit خودتان Validate کنید. Locale بهتنهایی صحت تجاری را تضمین نمیکند.
تفاوت داده مصنوعی و داده ماسکشده چیست؟
Masked data از Source موجود Transform میشود و ممکن است Structure/Linkability آن را حفظ کند. Synthetic data رکوردهای تازه را با Rule یا مدل میسازد، اما اگر مدل از Production یاد گرفته باشد هنوز Privacy risk محتمل است. Source، Threat model، utility و re-identification risk را جدا ارزیابی کنید.
چگونه داده تصادفی را قابل بازتولید کنیم؟
Seed، نسخهٔ دقیق Generator/Library، Spec hash، ترتیب و Config را در Manifest ثبت کنید و خروجی canonical یا minimal failing example را نگه دارید. Seed تنها کافی نیست، چون Provider و الگوریتم ممکن است با Update تغییر کند.
چه زمانی از Property-Based Testing استفاده کنیم؟
وقتی Domain ورودی بزرگ و یک Invariant روشن دارید؛ مانند «مجموع Refund از Payment بیشتر نمیشود». Strategy معتبر/نامعتبر را دقیق بسازید و Shrinking/Replay را فعال کنید. برای Workflow، Rule خاص یا UX همچنان Example و Testهای سناریومحور لازماند.
منابع معتبر برای مطالعهٔ بیشتر
- Faker — Seed، Unique، Version و Providerها
- Faker — مستندات locale فارسی ایران
- Hypothesis — Strategy generation و shrinking
- Schemathesis — Schema-based property testing برای API
- NIST ACTS — t-way، constraint و covering arrays
- JSON Schema — Type و constraintهای تولید/اعتبارسنجی
- OpenAPI ۳.۱.۱ — Schema و examples
- NIST SP ۸۰۰-۱۸۸ — De-identification و Privacy risk
- ICO — تفاوت anonymisation و pseudonymisation

