BDD از فایل .feature شروع نمیشود؛ از یک سؤال حلنشده شروع میشود. اگر Product، Developer و Tester دربارهٔ «بازپرداخت تکراری» برداشتهای متفاوتی دارند، نوشتن ده Scenario با Cucumber این اختلاف را پنهان میکند، نه حل. ابتدا Rule و Example را کشف کنید؛ بعد آنها را خوانا Formulate کنید؛ فقط مثالهای ارزشمند و پایدار را Automate کنید.
این راهنما BDD با Cucumber را از گفتوگوی کسبوکار تا Gherkin و Step Definition آموزش میدهد. مثال اصلی یک بازپرداخت فروشگاه ایرانی است و نشان میدهد چگونه سناریوی رفتارمحور بنویسیم، Cucumber.js را اجرا کنیم، State هر Scenario را ایزوله نگه داریم و از ضدالگوهای UI script، Stepهای تکراری و «مستندات زندهٔ مرده» دور بمانیم.
BDD چیست و چه ربطی به Cucumber دارد؟
Behavior-Driven Development شیوهای برای ساخت درک مشترک دربارهٔ رفتار مطلوب سیستم با مثالهای واقعی است. مستندات رسمی BDD در Cucumber سه Practice را بهترتیب معرفی میکند:
- Discovery: Ruleها، مثالها و سؤالهای ناشناخته را در گفتوگو کشف کنید.
- Formulation: مثالهای توافقشده را دقیق، خوانا و قابلآزمون بیان کنید.
- Automation: Specification ارزشمند را به سیستم وصل و رفتار واقعی را بررسی کنید.
Cucumber ابزار مرحلهٔ Automation است، نه خود BDD. میتوان BDD بدون Cucumber داشت و میتوان Cucumber داشت اما هیچ Discovery و فهم مشترکی نداشت. اگر تیم فقط Testهای قدیمی را به Given/When/Then ترجمه کند، Syntax عوض شده ولی رفتارمحوری ایجاد نشده است.
هدف BDD چیست؟
- ابهام را پیش از پیادهسازی با مثال آشکار کند؛
- زبان دامنه را میان Product، Business، Development و Test همگرا کند؛
- Ruleهای کسبوکار را از جزئیات UI/کد جدا کند؛
- Scope یک Change را با مثالهای ضروری کوچک کند؛
- سؤالهای بیپاسخ و تصمیمگیرندهٔ آنها را مرئی کند؛
- برای رفتارهای مهم، Regression قابلفهم بسازد؛
- Specification و کد را با Review و Automation نزدیک نگه دارد.
BDD کیفیت، هزینه یا رضایت را تضمین نمیکند. ارزش آن به کیفیت Discovery، پایداری Rule، طراحی Automation و مشارکت واقعی خوانندگان بستگی دارد.
چرخه Discovery → Formulation → Automation

Discovery؛ چه رفتاری و چرا؟
یک Change کوچک و نزدیک را بردارید. افراد دارای سه Perspective لازماند: Business/Product، Development و Test. نام «Three Amigos» به معنای دقیقاً سه نفر یا جلسهٔ اجباری نیست؛ هدف حضور Perspectiveها و تصمیمگیرندهٔ مناسب است.
پرسش شروع:
کاربر میخواهد بخشی از مبلغ سفارش پرداختشده را پس بگیرد؛ سیستم در Retry، مبلغ مرزی و Callback دیرهنگام چگونه رفتار میکند؟
راهنمای تحلیل نیازمندیها برای شناخت Stakeholder، Oracle، داده و محدودیتها مکمل Discovery است.
Formulation؛ چه باید بکند؟
مثالهایی را که تیم پذیرفته است با زبان دامنه و Outcome مشاهدهپذیر بنویسید. Formulation جلسهٔ ترجمهٔ Test case به Gherkin نیست؛ Editing مشترک برای حذف ابهام و جزئیات تصادفی است.
Automation؛ واقعاً چه میکند؟
Cucumber متن Step را به Step Definition متصل میکند. Definition از Driver دامنه/API/UI استفاده و Assertion میکند. Automation باید روی کوچکترین مرز قابلاعتماد اجرا شود؛ BDD الزام نمیکند همهٔ Scenarioها از Browser عبور کنند.
این ترتیب با Shift-left Testing همراستاست: Example زودتر از کد Feedback میدهد، سپس Test خودکار Behavior را پس از هر Change بررسی میکند.
Example Mapping با Rule، Example و Question
| نوع کارت | بازپرداخت |
|---|---|
| Story/Outcome | بازپرداخت امن و قابلپیگیری برای سفارش پرداختشده |
| Rule | مجموع بازپرداخت نباید از مبلغ پرداخت معتبر بیشتر شود |
| Example | پرداخت ۱٬۰۰۰٬۰۰۰ IRR؛ Refund اول ۲۵۰٬۰۰۰؛ Refund دوم ۷۵۰٬۰۰۰ → هر دو پذیرفته |
| Example مرزی | پس از آن Refund یک IRR → رد |
| Question | کارمزد یا تغییر نرخ ارز در سقف وارد میشود؟ |
| Out of scope | بازپرداخت بینبانکی چندروزه در Story جدا |
Rule را از Example استخراج کنید؛ سپس Example مخالف و مرزی پیدا کنید. سؤال بیپاسخ را Scenario ساختگی نکنید. Owner و موعد تصمیم را ثبت کنید. Discovery خوب گاهی باعث میشود Story کوچک یا متوقف شود؛ این شکست نیست، Feedback زودهنگام است.
Gherkin چیست؟
Gherkin زبان ساختیافتهٔ Feature file است. مرجع رسمی Gherkin Keywordهای اصلی را تعریف میکند:
| Keyword | نقش |
|---|---|
| Feature | Capability/دامنهٔ کلی؛ هر فایل یک Feature |
| Rule | یک Rule کسبوکار و مثالهای مرتبط |
| Scenario / Example | یک مثال مشخص از رفتار |
| Given | Context/State مرتبط |
| When | Event یا Action اصلی |
| Then | Outcome مشاهدهپذیر |
| Background | Context کوتاه و مشترک پیش از هر Scenario |
| Scenario Outline + Examples | یک Rule با چند مجموعه دادهٔ معنادار |
| Data Table / Doc String | دادهٔ ساختاریافته یا متن چندخطی |
| Tags | Metadata و انتخاب Scope اجرا |
And و But برای خواناییاند؛ Cucumber هنگام Match کردن Step Definition معنای فنی متفاوتی برای Given/When/Then/And/But قائل نمیشود. معنا را جمله و جایگاه آن میسازد.
یک Feature خوب برای بازپرداخت
Feature: بازپرداخت سفارش پرداختشده
برای جلوگیری از اثر مالی تکراری
درخواست بازپرداخت باید با Ruleهای مبلغ و Idempotency پردازش شود
Rule: مجموع بازپرداخت از مبلغ پرداخت معتبر بیشتر نمیشود
Scenario: بازپرداخت بخشی از مبلغ پرداخت
Given سفارش "O-1405-101" به مبلغ 1000000 IRR پرداخت شده است
When بازپرداخت 250000 IRR با کلید "R-101" درخواست میشود
Then بازپرداخت به مبلغ 250000 IRR پذیرفته میشود
And مانده قابل بازپرداخت سفارش 750000 IRR است
Scenario: تکرار همان درخواست اثر مالی دوباره نمیسازد
Given درخواست بازپرداخت "R-101" قبلاً پذیرفته شده است
When همان درخواست بازپرداخت دوباره ارسال میشود
Then همان نتیجه قبلی برگردانده میشود
And برای "R-101" فقط یک اثر مالی وجود دارد
Scenario: مبلغ بیش از مانده رد میشود
Given مانده قابل بازپرداخت سفارش 750000 IRR است
When بازپرداخت 750001 IRR درخواست میشود
Then درخواست با دلیل "exceeds_refundable_amount" رد میشود
چرا این Scenarioها رفتارمحورند؟
- از «روی دکمه کلیک کن» یا CSS selector خبری نیست.
- Rule و Outcome کسبوکار روشناند.
- عددها Example واقعی و مرزی میسازند.
- IRR صریح است و با تومان اشتباه نمیشود.
- Idempotency با اثر مالی سنجیده میشود، نه فقط HTTP ۲۰۰.
- پیام فنی/دامنهٔ قابلپایدارشدن در Oracle آمده است.
اگر Refund Stateهای متعدد دارد، مدل آن را پیش از سناریو با تست انتقال حالت روشن کنید.
Scenario ضعیف و نسخه بهتر
ضدالگوی UI script
Scenario: Refund
Given I open the orders page
And I click the first row
And I click the blue refund button
And I type "250000" in the amount input
When I click submit
Then I see a green toast
این متن Layout و Widget را مستند میکند، اما Rule و Outcome مالی را نمیگوید. با تغییر رنگ دکمه Specification میشکند و ممکن است Toast سبز باشد ولی Ledger دوبار نوشته شود.
نسخهٔ رفتارمحور
Scenario: بازپرداخت جزئی سفارش پرداختشده
Given یک سفارش پرداختشده با مانده قابل بازپرداخت 1000000 IRR
When کارشناس مجاز بازپرداخت 250000 IRR را ثبت میکند
Then مانده قابل بازپرداخت 750000 IRR میشود
And یک رکورد بازپرداخت قابلپیگیری ایجاد میشود
Automation میتواند این Scenario را در API/Component layer اجرا کند و یک یا دو E2E جدا مسیر UI حیاتی را بسنجد.
قواعد نوشتن Given، When و Then
Given؛ Context کافی، نه Setup فنی
Given باید State مرتبط را بگوید: «سفارش پرداخت شده است». جزئیات ساخت جدول، Login token، Endpoint و Cleanup در Glue/Driver میماند. Given طولانی نشانهٔ Scope بزرگ یا زبان دامنهٔ ضعیف است.
When؛ Event اصلی
ترجیحاً یک When اصلی داشته باشید. چند When پشت سر هم غالباً چند رفتار را در یک Scenario مخلوط میکند. اگر Journey واقعاً چند Event دارد، Outcomeهای میانی و دلیل Business را روشن کنید.
Then؛ Outcome مشاهدهپذیر
Then نباید Implementation را Assert کند: «متد X فراخوانی شد» Specification کسبوکار نیست. نتیجه میتواند State، Event، Response، Ledger یا پیام کاربر باشد. یک Oracle لایهٔ مناسب انتخاب کنید.
Rule، Background و Scenario Outline را درست استفاده کنید
Rule برای سازماندهی Business rule
یک Feature بزرگ را با Ruleهای مشخص تقسیم کنید: «سقف مبلغ»، «اختیار نقش»، «Idempotency». Scenarioها زیر Rule باید آن را مثال بزنند، نه Featureهای پراکنده را.
Background کوتاه و قابلحفظ
Background قبل از هر Scenario اجرا میشود. مستندات رسمی توصیه میکند Context پیچیده و نامرتبط را آنجا پنهان نکنید و آن را کوتاه نگه دارید. Database cleanup، Browser launch و WireMock lifecycle معمولاً Hook/Support فنیاند؛ «کاربر فروشندهٔ مجاز است» ممکن است Background خوانا باشد.
Scenario Outline فقط برای Variation معنادار
Scenario Outline: مبلغ نامعتبر بازپرداخت رد میشود
Given مانده قابل بازپرداخت 750000 IRR است
When بازپرداخت <amount> IRR درخواست میشود
Then درخواست با دلیل "<reason>" رد میشود
Examples:
| amount | reason |
| 0 | amount_must_be_positive |
| -1 | amount_must_be_positive |
| 750001 | exceeds_refundable_amount |
Examples table را به Cartesian product صدها Row تبدیل نکنید. Equivalence partition و Boundaryهای توضیحدهنده را نگه دارید؛ دادهٔ حجیم بهتر است در تست پایینتر Data-driven باشد.
Data Table و Doc String
Data Table وقتی خوب است که Business واقعاً یک مجموعهٔ ساختاریافته را میخواند:
Given سفارش شامل اقلام زیر است
| sku | quantity | unit_price_irr |
| A-101 | 2 | 150000 |
| B-220 | 1 | 400000 |
Dump دیتابیس، JSON عظیم یا Payload فنی را داخل Feature نریزید. Doc String برای متن معنادار دامنه مانند Template پیام مناسب است؛ Fixture حجیم را Versioned و ارجاعپذیر نگه دارید.
Gherkin فارسی و RTL
Cucumber Localization فارسی را پشتیبانی میکند. در فهرست رسمی زبانها، با Header زیر میتوان Keywordهای فارسی را فعال کرد:
# language: fa
وِیژگی: بازپرداخت سفارش
Rule: سقف بازپرداخت
مثال: مبلغ بیشتر از مانده
با فرض مانده قابل بازپرداخت 750000 IRR است
هنگامی بازپرداخت 750001 IRR درخواست میشود
آنگاه درخواست رد میشود
در Localization فعلی، Rule برای فارسی همچنان همان Keyword انگلیسی است. تیم میتواند Keywordهای انگلیسی و جملههای فارسی را برای سازگاری بهتر Editor انتخاب کند؛ مهم ثبات Repository است.
نکات فارسی
- Encoding را UTF-۸ و Direction نمایش Editor را آزمایش کنید.
- اعداد Example را ترجیحاً ASCII نگه دارید یا ParameterType برای ۱۲۳/۱۲۳ ثبت کنید؛ فرض نکنید
{int}همه شکلهای رقم را میپذیرد. - Normalization «ی/ی»، «ک/ک» و نیمفاصله را در Domain driver روشن کنید.
- IRR/IRT را جدا بنویسید و تبدیل را Step پنهان نکنید.
- کلید Idempotency، Token، شماره واقعی و دادهٔ شخصی را در Feature commit نکنید.
- اگر Translation اصطلاح دامنه را مبهم میکند، اصطلاح پایدار انگلیسی را در Glossary نگه دارید.
نصب Cucumber.js
برای پروژهٔ Node.js از نسخهٔ نگهداریشدهٔ Node و نسخهٔ سازگار Cucumber استفاده کنید. README رسمی Cucumber.js Package و Command پایه را نشان میدهد:
npm install --save-dev @cucumber/cucumber
npx cucumber-js
نسخهٔ Package و Node را در Lockfile/CI Pin کنید. Command، Module system و TypeScript loader ممکن است میان نسخهها تغییر کند؛ مستندات همان Version را بررسی کنید.
ساختار پیشنهادی
features/
refund.feature
step_definitions/
refund.steps.js
support/
hooks.js
world.js
test-support/
drivers/
refund-driver.js
Feature زبان کسبوکار است؛ Step Definition Glue نازک؛ Driver تعامل فنی با API/Component/DB stub را کپسوله میکند.
Step Definition چیست؟
مستندات Step Definition آن را Method/Function دارای Expression میداند که به Stepهای Gherkin متصل میشود. Cucumber Expressions خواناتر از Regex پیچیدهاند:
const assert = require('node:assert/strict')
const { Given, When, Then } = require('@cucumber/cucumber')
Given(
'سفارش {string} به مبلغ {int} {word} پرداخت شده است',
async function (orderId, amount, currency) {
await this.refundDriver.createPaidOrder({ orderId, amount, currency })
this.orderId = orderId
}
)
When(
'بازپرداخت {int} {word} با کلید {string} درخواست میشود',
async function (amount, currency, idempotencyKey) {
this.response = await this.refundDriver.requestRefund({
orderId: this.orderId,
amount,
currency,
idempotencyKey
})
}
)
Then(
'مانده قابل بازپرداخت سفارش {int} {word} است',
async function (expectedAmount, currency) {
const order = await this.refundDriver.getOrder(this.orderId)
assert.equal(order.refundableAmount, expectedAmount)
assert.equal(order.currency, currency)
}
)
در Cucumber.js اگر State از this/World استفاده میکنید، Arrow function ننویسید چون this را Lexical bind میکند. Step باید Assertion واقعی داشته باشد؛ مستندات رسمی میگوید صرف return false Step را Fail نمیکند، باید Error/Assertion رخ دهد.
معماری Step Definition قابلنگهداری
| لایه | مسئولیت | نباید |
|---|---|---|
| Feature | Rule و Example دامنه | Selector، SQL، timeout فنی |
| Step definition | Parameter → Driver call → Assertion | Business logic یا workflow حجیم |
| Task/Domain driver | عملیات معنادار مانند requestRefund | به متن Gherkin وابسته شود |
| Adapter | API/UI/DB stub/queue client | Oracle کسبوکار را جعل کند |
| World | State همان Scenario | Global state مشترک بین Scenarioها |
تست API در بسیاری از رفتارهای کسبوکار سریعتر و پایدارتر از UI است؛ راهنمای تست API Contract، authorization و Idempotency را پوشش میدهد.
Stepهای خوب و بد
| بد | بهتر | دلیل |
|---|---|---|
| I click “#refund-blue” | کارشناس مجاز بازپرداخت را ثبت میکند | Intent، نه selector |
| the API returns 200 | بازپرداخت پذیرفته و قابلپیگیری است | Outcome دامنه |
| database row count is 1 | درخواست تکراری یک اثر مالی دارد | Invariant |
| I wait 5 seconds | پردازش بازپرداخت تکمیل میشود | Poll/event با timeout در Driver |
| user A exists | فروشنده مجاز بازپرداخت دارد | Role معنادار |
Step را بهخاطر «Reuse» بیش از حد عمومی نکنید. عبارت I perform action {string} on entity {string} زبان دامنه را از بین میبرد. Reuse نتیجهٔ Vocabulary مشترک است، نه هدف مستقل.
Ambiguous و Duplicate Step Definition
اگر بیش از یک Expression یک Step را Match کند، Cucumber آن را Ambiguous گزارش میکند. راهکار:
- Vocabulary دامنه را استاندارد کنید؛
- Expressionهای بیشازحد عمومی را محدود کنید؛
- Step catalog را Search/Review کنید؛
- ParameterType معنادار بسازید؛
- Glue را بر اساس Domain سازمان دهید، نه Feature fileهای تصادفی؛
- Duplicate را با Rename/Migration هماهنگ حذف کنید.
Regex هوشمند ولی ناخوانا هزینهٔ نگهداری میسازد. Cucumber Expressions Typeهای داخلی و Custom را برای Match خواناتر فراهم میکند.
State، World و استقلال Scenario
اصل رسمی Cucumber دربارهٔ State روشن است: بین Scenarioها State به اشتراک نگذارید. هر Scenario باید مستقل، قابلاجرای تنها و قابلموازیشدن باشد.
- Global/static variable نداشته باشید؛
- World فقط عمر همان Scenario را داشته باشد؛
- Database را با Namespace/transaction/fixture ایزوله Seed کنید؛
- Cookie/session هر Scenario جدا باشد؛
- Scenarioها به ترتیب فایل وابسته نباشند؛
- Cleanup فقط دادهٔ همان Test run را حذف کند؛
- Retry Scenario اول را پنهان نکند.
راهنمای رسمی State نیز از اشتراک State میان Scenarioها منع میکند. طراحی دادهٔ Seed/Reset و دادهٔ مصنوعی در مدیریت داده تست آمده است.
Hooks؛ Setup فنی، نه رفتار پنهان
Before/After برای Browser، Driver، DB cleanup، Screenshot و Attachment فنی مناسباند. اما Hook برای خوانندهٔ Feature نامرئی است. اگر Context برای فهم رفتار مهم است، آن را Given/Background بیان کنید.
Hookهای Tag-based میتوانند Setup متفاوت بسازند، ولی زیادشدن آنها ترتیب و State پنهان میسازد. Lifecycle، ترتیب، timeout و failure cleanup را Test کنید.
BDD در چه سطحی Automate شود؟
| رفتار | مرز پیشنهادی |
|---|---|
| محاسبه سقف Refund | Unit/property tests؛ لازم نیست همه در Cucumber باشند |
| Idempotency و state/ledger | Component/API + DB ایزوله |
| Contract درگاه | Consumer/provider contract یا Stub رسمی |
| Journey کارشناس | چند E2E حیاتی |
| ظاهر/focus/RTL | UI/accessibility tests |
| Reconciliation زیر بار | Integration/performance lane |
BDD جای Test design، Exploration و لایههای دیگر را نمیگیرد. اتوماسیون تست باید براساس ارزش، پایداری و TCO انتخاب شود.
Tags و اجرای CI
@payment @refund @component
Feature: بازپرداخت سفارش پرداختشده
@smoke
Scenario: بازپرداخت جزئی معتبر
...
@wip
Scenario: مغایرت مبلغ Callback
...
Tag را Metadata معنیدار نگه دارید: Domain، risk، execution layer یا temporary workflow. ضدالگوها:
- Tag هر Sprint/Person؛
@smokeروی صدها Scenario؛@wipبدون Owner/expiry؛- اجرای UI همهٔ Featureها روی هر Commit؛
- استفاده از Tag برای پنهانکردن Testهای دائماً Fail.
Lane پیشنهادی:
| رویداد | Scope | خروجی |
|---|---|---|
| PR | Feature/Rule مرتبط + Component سریع | Feedback تشخیصی |
| Merge | Contract/Integration گستردهتر | Artifact confidence |
| Nightly | E2E و Matrix کند | Triage با Owner |
| Release | Journey حیاتی و policy | Risk decision، نه تضمین |
Living Documentation چه زمانی واقعاً زنده است؟
Feature file خودکار به مستند معتبر تبدیل نمیشود. Living documentation نیاز دارد:
- Rule و Example با Business/Product Review شوند؛
- Automation روی رفتار واقعی اجرا شود؛
- Feature مرده/منسوخ حذف یا Archive شود؛
- Report برای مخاطب قابلدسترسی و Searchable باشد؛
- Version/Build و آخرین نتیجه مشخص باشد؛
- Undefined/Pending/Ambiguous Scenario پذیرفتهٔ خاموش نباشد؛
- Feature از UI detail و Fixture noise پاک بماند.
سبز بودن فقط یعنی Step Definitionها بدون Error اجرا شدند؛ Oracle ضعیف میتواند سند غلط را سبز نگه دارد.
BDD، TDD، ATDD و Specification by Example
| رویکرد | سؤال غالب | خروجی |
|---|---|---|
| BDD | رفتار ارزشمند و Rule مشترک چیست؟ | Discovery + example + executable spec |
| TDD | کوچکترین رفتار کد و Design بعدی چیست؟ | Red/Green/Refactor در سطح پیادهسازی |
| ATDD | چه مثال پذیرشی Change را قابلقبول میکند؟ | Acceptance examples پیش از Implementation |
| Specification by Example | چگونه Rule را با مثال دقیق و زنده کنیم؟ | مثالهای مشترک و مستندشده |
| Cucumber | چگونه Gherkin را به Automation وصل کنیم؟ | Runner، glue، report |
مرزها همپوشانی دارند و تیمها اصطلاحها را متفاوت بهکار میبرند. BDD جای TDD نیست؛ Cucumber نیز الزاماً Acceptance level نیست. همان Rule میتواند Cucumber Component test و چند Unit test زیرین داشته باشد.
چه زمانی Cucumber انتخاب خوبی نیست؟
- هیچکس خارج از Automation code Featureها را نمیخواند یا Review نمیکند؛
- Domain بسیار فنی و Unit/API test مستقیم خواناتر است؛
- Rule هنوز ناپایدار و محصول در Spike/Discovery اولیه است؛
- تیم فقط میخواهد UI scriptهای موجود را بازنویسی کند؛
- دادهٔ ترکیبی بسیار حجیم در Testهای پایینتر بهتر پوشش میگیرد؛
- هزینهٔ Glue/Runtime/Report از ارزش Communication بیشتر است.
در این حالت Discovery و Example mapping را نگه دارید، ولی Automation را با ابزار سادهتر انجام دهید. BDD به Cucumber وابسته نیست.
مثال کامل Workflow تیم
۱. پیش از Refinement
Product Story کوچک، Outcome و Known rules را آماده میکند؛ تصمیمهای باز فهرست میشوند.
۲. Discovery سیدقیقهای
Product، Developer و Tester Rule/Example/Question میسازند؛ Security/Operations در Risk لازم وارد میشوند. Story بزرگ شکسته میشود.
۳. Formulation مشترک
دو یا سه Example مهم به Gherkin رفتارمحور تبدیل و در Pull request جدا Review میشوند. Duplicate vocabulary بررسی میشود.
۴. Automation-first
Scenario اول Undefined/Fail میشود؛ Step Definition نازک و Driver دامنه ساخته میشود؛ Implementation تا Pass شدن Behavior توسعه مییابد. Testهای پایینتر برای Ruleهای ترکیبی اضافه میشوند.
۵. Exploration و NFR
Exampleهای کشفشده Seed تست اکتشافی، Accessibility، Security و Performance میشوند؛ همه به Cucumber تبدیل نمیشوند.
۶. CI و Documentation
Scenario مرتبط در PR، Suite بزرگتر در Merge/Nightly و Report Versioned منتشر میشود. Failure Owner و Artifact دارد.
۷. Review و یادگیری
اگر Production Rule جدیدی آشکار کرد، اول Discovery/Example بهروزرسانی میشود، سپس Regression مناسب ساخته میشود. این حلقه با تست چابک و Whole-team quality سازگار است.
متریکهای مفید BDD/Cucumber
| متریک | پرسش | دام |
|---|---|---|
| Question-to-decision time | Discovery ابهام را واقعاً حل میکند؟ | تعداد سؤال بهعنوان KPI |
| Scenario review participation | مخاطب Business/Product میخواند؟ | حضور اجباری نمایشی |
| Undefined/Pending/Ambiguous aging | Specification ناقص بیمالک مانده؟ | حذف برای سبزکردن |
| Execution p95 و flaky rate | Feedback قابلاعتماد و بهموقع است؟ | Retry پنهان |
| Duplicate/unused steps | Vocabulary و Glue سالماند؟ | هدف صفر بدون Context |
| Living-doc freshness | Feature منسوخ یا بینتیجه داریم؟ | Timestamp بدون Review |
| Recurring rule misunderstanding | Exampleها Learning ساختهاند؟ | سرزنش نقشها |
Scenario count، Step reuse percentage و «تعداد Featureهای خودکار» Outcome نیستند و قابلبازیاند. متریکهای تست نرمافزار را به تصمیم و Guardrail وصل کنید.
اشتباههای رایج BDD با Cucumber
- Automation قبل از Discovery: اختلاف فهم در کد Glue دفن میشود.
- Gherkin = Test case prose: Given/When/Then رفتارمحوری ایجاد نمیکند.
- UI script: Feature با Selector و Click شکننده میشود.
- Then فنی: HTTP ۲۰۰ یا Row count جای Outcome دامنه مینشیند.
- Background بلند: Context بیرون صفحه و نامرئی میشود.
- Scenario Outline عظیم: جدول داده جای Rule/Example را میگیرد.
- Step بسیار عمومی: Vocabulary از بین میرود و Ambiguity بالا میرود.
- Business logic در Glue: Test و محصول دو پیادهسازی متفاوت میسازند.
- State مشترک: ترتیب Scenario و اجرای موازی Test را Flaky میکند.
- Hook پنهان: Context مهم برای خواننده نامرئی است.
- همهچیز Cucumber: Unit/Property/Performance/Exploration نادیده میروند.
- Living docs تضمینی: Feature منسوخ ولی سبز باقی میماند.
- Secret در Example: دادهٔ واقعی وارد Git و Report میشود.
چکلیست نهایی BDD و Cucumber
- Discovery پیش از Formulation و Automation انجام شده است.
- Rule، Example، Question و Out-of-scope جدا هستند.
- مخاطب Business/Product متن Feature را Review میکند.
- Scenarioها از زبان دامنه و Outcome قابلمشاهده استفاده میکنند.
- جزئیات UI، SQL، Token و Setup فنی در Feature نیست.
- Given Context، When Event و Then Outcome روشن دارند.
- Background کوتاه و Scenarioها مستقلاند.
- Outline فقط Variationهای معنادار را پوشش میدهد.
- Step Definition نازک و Driver/Adapter جداست.
- Assertion Error واقعی میسازد و Oracle مستقل است.
- World/داده برای هر Scenario ایزوله و Cleanup محدود است.
- Tag، Hook، Pending و Retry Owner/Policy دارند.
- فارسی/RTL/رقم/واحد پول در Parser و Domain تست شدهاند.
- Report Version/Build و Freshness قابلمشاهده دارد.
- Scenario فقط وقتی Automate میشود که ارزش Regression/Communication دارد.
سوالات متداول BDD با Cucumber
BDD به زبان ساده چیست؟
BDD شیوهٔ همکاری برای کشف و بیان رفتار مطلوب سیستم با مثالهای واقعی است. چرخهٔ آن Discovery، Formulation و Automation است؛ هدف اصلی فهم مشترک و نرمافزار ارزشمند است، نه صرفاً تولید Test خودکار.
تفاوت BDD، Gherkin و Cucumber چیست؟
BDD فرایند/Practice همکاری و توسعه است؛ Gherkin زبان ساختیافتهٔ Feature file؛ Cucumber ابزاری است که Stepهای Gherkin را به کد Automation وصل و اجرا میکند. استفاده از Cucumber بهتنهایی به معنای اجرای BDD نیست.
آیا میتوان Gherkin را فارسی نوشت؟
بله. Cucumber Localization فارسی و Header # language: fa را پشتیبانی میکند. Keywordهای فارسی رسمی را از نسخهٔ مستندات بررسی کنید؛ Encoding/RTL و اعداد فارسی را نیز در Editor و ParameterType آزمایش کنید.
آیا هر Scenario باید از UI اجرا شود؟
خیر. مرز Automation را از Risk انتخاب کنید. بسیاری از Ruleهای کسبوکار در Component/API سریعتر و پایدارترند؛ چند Journey حیاتی میتواند E2E باشد و Ruleهای محاسباتی Testهای Unit/Property جدا داشته باشند.
چرا Cucumber Scenario سبز است ولی محصول غلط کار میکند؟
سبزشدن فقط نشان میدهد Stepها بدون Error پایان یافتهاند. Oracle ضعیف، Assertion ناقص، Fake غیرنماینده یا بررسی HTTP ۲۰۰ بهجای Outcome میتواند False confidence بسازد. Then را به Invariant واقعی و Evidence مستقل وصل کنید.
منابع و یادداشت بازبینی
چرخهٔ BDD، Gherkin/Rule/Background/Outline، Localization فارسی، Cucumber Expressions، Step Definition، Assertion، Hook و Scenario state با مستندات رسمی Cucumber و Cucumber.js تطبیق داده شدهاند. آخرین بازبینی محتوایی: ۱۵ مرداد ۱۴۰۵. API و Configuration ابزار ممکن است با نسخه تغییر کنند؛ Package/Node را Pin و مستندات همان Release را مرجع کنید.
جمعبندی: BDD زمانی ارزش دارد که مثال گفتوگو را بهتر کند، نه وقتی Gherkin لایهای روی Automation قدیمی باشد. Rule و سؤال را در Discovery پیدا کنید، Example را بدون جزئیات تصادفی بنویسید، Glue را نازک و Scenario را مستقل نگه دارید و فقط Specificationهایی را Automate کنید که برای فهم یا Regression ارزش پایدار دارند.

