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 مهم است. جریان امن‌تر چنین است:

  1. Source و Data owner را مشخص و استفاده را مجاز کنید.
  2. Direct identifier، quasi-identifier، sensitive field و secret را کشف کنید.
  3. Subset را از Entity ریشه با Closure روابط لازم استخراج کنید.
  4. Transformهای سازگار و referentially consistent اعمال کنید.
  5. Utility، Domain invariant و Distribution هدف را اعتبارسنجی کنید.
  6. Re-identification/privacy risk را مستقل ارزیابی کنید.
  7. Dataset را در مسیر محدود، رمز‌شده و Auditشده تحویل دهید.
  8. 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 واقعی ولی کم‌ریسک انتخاب و این سناریوها را اجرا کنید:

  1. سه Entity مرتبط با Cross-field rule بسازید.
  2. Boundary و invalid single-fault تولید کنید.
  3. با یک Seed دو بار خروجی canonical یکسان بگیرید.
  4. Schema را تغییر دهید و هزینهٔ نگهداری را بسنجید.
  5. صد هزار/یک میلیون Row با profile هدف بسازید.
  6. Failure را به minimal/replayable example برسانید.
  7. PII/secret scan و access audit اجرا کنید.
  8. Pipeline قطع، retry و cleanup failure را شبیه‌سازی کنید.
  9. در شبکه و محدودیت پرداخت/دسترسی ایران، 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های سناریومحور لازم‌اند.

منابع معتبر برای مطالعهٔ بیشتر

دیدگاهتان را بنویسید