Given-When-Then — یک الگوی ساختاری برای توصیف سناریوهای تست است که توسط BDD از domain-driven design گرفته شده و برای Behaviour-Driven Development تطبیق داده شده است. این قالب سناریو را به سه بخش منطقی تقسیم میکند: پیششرطها (Given)، اقدام (When) و نتیجه مورد انتظار (Then). به گفته Martin Fowler (2023)، Given-When-Then نه فقط یک قالب تست، بلکه ابزاری برای تفکر است که تحلیل نیازمندیها و طراحی سناریوها را قبل از شروع پیادهسازی منظم میکند.
نکات کلیدی
Given-When-Then — الگویی برای توصیف رفتار است که اولین بار توسط Dan North در سال ۲۰۰۶ به عنوان بخشی از روششناسی Behavior-Driven Development تدوین شد. این الگو مشکل توصیفهای بدون ساختار سناریوهای تست را حل میکند که اغلب شامل ترکیبی از پیششرطها، اقدامات و بررسیها به ترتیب دلخواه هستند.
ایده اصلی الگو تفکیک مسئولیتها بین سه بلوک است. هر بلوک دقیقاً مسئول یک جنبه از سناریو است: وضعیت قبل، رویداد در حین و بررسی بعد از آن. این کار سناریو را خوانا، قابل بررسی و قابل خودکارسازی میکند. بر اساس تحقیق توسعهدهندگان فریمورک Cucumber (۲۰۲۴)، سناریوهایی که به طور دقیق از الگوی Given-When-Then پیروی میکنند، ۴۲٪ زمان کمتری برای درک توسط یک عضو جدید تیم نیاز دارند.
Dan North ایده ساختار سهبخشی را از formulation of tests در TDD و روششناسی Test-by-Example (ایجاد شده توسط Brian Marick) اقتباس کرد. ماریک پیشنهاد کرد که نیازمندیها از طریق مثالهایی (examples) توصیف شوند که همزمان به عنوان تست نیز عمل میکنند. Given-When-Then این ایده را رسمی کرد و مثالهای بدون ساختار را به یک الگوی قابل تکرار تبدیل نمود.
الگوی Given-When-Then نه تنها در سناریوهای BDD در Gherkin، بلکه در تستهای واحد معمولی در JUnit، XCTest و سایر فریمورکها نیز کاربرد دارد. کامنتهایی در کد که تست را به سه بلوک تقسیم میکنند، یک روش رایج برای بهبود خوانایی پایگاه تست است. Google این رویکرد را در کتاب خود “Software Engineering at Google” (۲۰۲۰) توصیه میکند.
هر بلوک Given-When-Then دارای معناشناسی و قوانین پر کردن کاملاً مشخصی است. نقض این قوانین منجر به سناریوهایی میشود که خودکارسازی یا درک آنها دشوار است.
بلوک Given وضعیت سیستم را قبل از اجرای اقدام مورد تست توصیف میکند. این شامل: اشیاء موجود (کاربر، سفارش، تنظیمات)، وضعیتهای فعال (احراز هویت شده، متصل به شبکه)، و همچنین مقادیر اولیه دادهها میشود. هر Given باید قابل بررسی باشد — اگر وضعیت سیستم با Given مطابقت نداشته باشد، سناریو باید نادیده گرفته شود یا محیط تست از قبل آماده شود.
بلوک When یک رویداد واحد را توصیف میکند که رفتار مورد تست را آغاز میکند. این میتواند فراخوانی یک متد، کلیک روی دکمه، دریافت اعلان یا پاسخ از سرور باشد. قانون کلیدی — یک When برای هر سناریو. اگر نیاز به بررسی دنبالهای از اقدامات است، سناریوهای جداگانه ایجاد میشوند، نه یک زنجیره When.
// Given: دادههای تست را ایجاد میکنیم
val user = User(email = "test@example.com", balance = 500.0)
val product = Product(price = 150.0)
// When: اقدام را انجام میدهیم
val result = PurchaseUseCase().buy(user, product)
// Then: نتیجه را بررسی میکنیم
assertEquals(PurchaseResult.Success, result)
assertEquals(350.0, user.balance)
بلوک Then بررسی میکند که سیستم به وضعیت مورد انتظار منتقل شده است. این شامل: مقادیر بازگشتی، تغییرات وضعیت اشیاء، فراخوانی سرویسهای خارجی (از طریق تأیید mock)، و همچنین تغییرات UI میشود. هر بلوک Then میتواند شامل چندین بررسی باشد، اما همه آنها به یک اقدام مربوط میشوند.
Given-When-Then و Arrange-Act-Assert (AAA) — دو نوع از یک الگوی سهبخشی هستند، اما با مخاطبان هدف متفاوت. درک تفاوتهای آنها به انتخاب قالب مناسب برای یک کار خاص کمک میکند.
| جنبه | Given-When-Then | Arrange-Act-Assert |
|---|---|---|
| ریشه | BDD، تحلیل کسبوکار | تست واحد |
| زبان | طبیعی (Gherkin) | کد (Kotlin, Swift, Java) |
| مخاطب | کل تیم + مشتری | توسعهدهندگان |
| سطح جزئیات | سطح بالا | جزئی |
| خودکارسازی | Cucumber, SpecFlow | JUnit, XCTest, Mockito |
الگوی Given-When-Then برای سناریوهایی که با مشتری یا تحلیلگر بحث میشوند بهینه است: معیارهای پذیرش ویژگیها، سناریوهای استفاده، بررسیهای رگرسیون. نحو Gherkin امکان نوشتن چنین سناریوهایی را بدون دانش برنامهنویسی فراهم میکند.
Arrange-Act-Assert — انتخاب طبیعی برای تستهای واحد است که یک متد یا کلاس خاص را بررسی میکنند. قالب AAA به فریمورکهای اضافی نیاز ندارد و در هر زبان برنامهنویسی کار میکند. برای توسعه iOS، Apple AAA را در مستندات XCTest (۲۰۲۴) توصیه میکند.
بیایید به مثالهای عملی Given-When-Then در Kotlin برای یک برنامه Android نگاه کنیم. مثال اول — تست سبد خرید با استفاده از MockK. مثال دوم — تست منطق اعلانهای push.
class CartTest {
fun `apply discount when total exceeds threshold`() {
// Given
val cart = Cart()
cart.addItem(Item("Laptop", price = 1000.0))
cart.addItem(Item("Mouse", price = 50.0))
val discount = DiscountCalculator(0.1)
// When
val total = discount.applyIfEligible(cart)
// Then
assertEquals(945.0, total)
assertTrue("Discount was not applied", total < 1050.0)
}
}
مثال دوم Given-When-Then را با کد ناهمگام نشان میدهد. در اینجا Given وضعیت Firebase Cloud Messaging را تنظیم میکند، When — دریافت اعلان push، Then — بررسی پردازش را انجام میدهد.
class PushNotificationTest {
fun `handle push notification when app in background`() = runTest {
// Given
val prefs = mockk<SharedPreferences>()
every { prefs.getString("token", null) } returns "fcm-token-abc"
val handler = PushHandler(prefs)
// When
val data = RemoteMessage().apply {
putData("type", "order_update")
putData("order_id", "123")
}
val result = handler.handleNotification(data)
// Then
assertEquals(NotificationAction.OpenOrder("123"), result)
}
}
مثال سوم — یک سناریوی BDD در Gherkin که Given-When-Then را در زمینه تستهای پذیرش نشان میدهد:
Feature: User Authorization
Scenario: User cannot login with expired token
Given the user has an expired refresh token
When they try to access the protected profile screen
Then they should see the login screen
And the app should clear all cached data
استفاده مؤثر از Given-When-Then مستلزم رعایت چندین روش اثبات شده است. آنها خوانایی، قابلیت نگهداری و خودکارسازی سناریوها را تضمین میکنند.
یک قانون سفت و سخت: یک سناریو — یک اقدام. اگر نیاز به بررسی دنبالهای از چند When دارید، چندین سناریو ایجاد کنید که در آن نتیجه قبلی به پیششرط بعدی تبدیل میشود. این کار سناریو را اتمی و قابل فهم میکند.
Given باید اصل مطلب را توصیف کند، نه اعداد خاص. به جای “Given کاربر ایوانف با موجودی ۵۰۰ روبل” — “Given کاربر با موجودی کافی”. دادههای خاص به Scenario Outline با جدول Examples منتقل میشوند. این کار سناریو را جهانی و قابل استفاده مجدد میکند.
ادغام سناریوهای Given-When-Then در خط لوله یکپارچهسازی مداوم آنها را از مستندات به محافظی در برابر رگرسیونها تبدیل میکند. هر درخواست merge در یک پروژه موبایل به طور خودکار سناریوهای BDD را اجرا میکند و در صورت شکست حداقل یک سناریو، ادغام را مسدود میکند.
سناریوهای BDD در Cucumber برای Android از طریق وظیفه Gradle ./gradlew cucumber اجرا میشوند. برای iOS (Quick/Nimble) — از طریق xcodebuild test. در سیستمهای CI (GitHub Actions, GitLab CI, Bitrise) تستهای BDD روی شبیهسازها یا دستگاههای واقعی اجرا میشوند. گزارش در قالب HTML تولید میشود که برای مدیران قابل فهم است: سناریوهای سبز — قبول، قرمز — شکست با ذکر مرحله.
فایلهای .feature در مخزن در کنار کد ذخیره میشوند و code review میشوند. تحلیلگر قبل از شروع توسعه یک درخواست merge با سناریوهای جدید ایجاد میکند (BDD-first). توسعهدهنده step definitions و پیادهسازی را مینویسد تا این سناریوها سبز شوند. وقتی همه سناریوها عبور میکنند — قابلیت آماده است. این رویکرد، که در کتاب Gojko Adzic “Specification by Example” (۲۰۱۱) توصیف شده است، نیازمندیها را به یک مصنوع قابل اجرا تبدیل میکند.
سوالات متداول
از نظر ساختار — بله، این همان الگوی سهبخشی است. تفاوت در مخاطب است: Given-When-Then به زبان کسبوکار گرایش دارد و در BDD با Gherkin استفاده میشود، در حالی که Arrange-Act-Assert یک قالب فنی برای تستهای واحد است. انتخاب به زمینه و تیم بستگی دارد.
محدودیتی وجود ندارد، اما توصیه میشود حداکثر ۳–۵ بررسی برای هر Then. اگر بررسیها بیشتر است، سناریو احتمالاً چیزهای زیادی را در یک اقدام آزمایش میکند. آن را به چند سناریو با Thenهای مختلف تقسیم کنید.
خیر. این الگو را میتوان در هر فریمورک تستی استفاده کرد، فقط با تقسیم تست با کامنتها یا خطوط خالی به سه بلوک. Gherkin فقط زمانی نیاز است که سناریوها در قالب فایل .feature برای Cucumber یا SpecFlow نوشته شوند.
توصیه میشود پیششرطهای تکراری را به Background (Gherkin) یا متدهای @Before (JUnit) منتقل کنید. اگر پیششرطها پیچیده هستند، از الگوی Builder برای ایجاد دادههای تست استفاده کنید. این کار Given را کوتاه و خوانا نگه میدارد.
خیر. When یک بلوک اجباری است که اقدام را توصیف میکند. اگر سناریو فقط وضعیت را بدون اقدام بررسی میکند (مثلاً “هنگام بارگذاری برنامه، دادهها باید کش شوند”)، When محرک را توصیف میکند: “وقتی برنامه راهاندازی میشود”.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید