Behavior-Driven Development(BDD)是一种通过自然语言描述系统行为来扩展TDD的开发方法。BDD场景以Given-When-Then格式编写,开发人员和业务分析师都能理解。根据Cucumber(2024)的数据,BDD消除了客户需求与实现之间的差距,将规范转化为可执行的测试。
要点
Behavior-Driven Development是2006年Dan North针对测试编写问题提出的TDD演进方案。在TDD中,开发人员编写测试,但“到底要测试什么”的问题仍然悬而未决。BDD通过将焦点从代码测试转移到系统行为描述来解决这个问题。
BDD的关键创新是为项目所有参与者提供通用语言。开发人员、测试人员、分析师和客户使用统一语言讨论场景,这些场景同时是可执行的测试。这消除了需求从分析师传递给开发人员时意义丢失的经典“传话游戏”问题。
Dan North于2006年在博客ThinkCode的文章「Introducing BDD」中阐述了BDD。他注意到TDD中的测试名称通常用实现术语(「testAddUser」)表述,而非行为术语(「user should be able to register with email」)。BDD将「test」替换为「should」,将「assert」替换为「expect」,将焦点转移到用户价值上。
根据剑桥大学(2021)的研究,在客户沟通中使用BDD场景的项目,与使用文本文档的传统规范相比,需求错误减少了35%。可执行的场景不允许模棱两可的表述 — 每个Given-When-Then要么通过,要么不通过。
Gherkin是Cucumber和SpecFlow框架用于描述行为场景的领域特定语言。Gherkin使用缩进和关键字来结构化场景,同时保持没有技术背景的人也能阅读。
Feature: Login
Scenario: Successful login with valid credentials
Given the user is on the login screen
When they enter valid username and password
Then they should see the home screen
Gherkin定义了几个基本的关键字。Feature描述功能,Scenario描述具体场景,Given描述前提条件,When描述操作,Then描述预期结果。此外,And和But用于组合多个条件。
Gherkin文件扩展名为.feature,存储在Android项目的src/test/resources/features/目录中。每个文件以Feature描述开头,后跟一个或多个Scenario。参数化使用Scenario Outline和Examples表格 — 这允许使用不同数据运行相同场景。
Feature: Calculator
Scenario Outline: Addition of two numbers
Given the calculator is running
When I add <a> and <b>
Then the result should be <result>
Examples:
| a | b | result |
| 2 | 3 | 5 |
| 0 | 0 | 0 |
| -1| 1 | 0 |
Given-When-Then是BDD从领域驱动设计中借鉴的场景描述结构模式。每个场景由三部分组成:前提条件、操作和预期结果。这种格式自然对应单元测试的Arrange-Act-Assert,但使用业务可理解的语言。
Given块描述场景开始前的系统状态:存在哪些数据、哪些组件活跃、应用程序的运行模式。在移动环境中,可能是「用户已登录」、「购物车不为空」或「设备处于离线模式」。
When块描述由用户或系统触发的事件:按钮点击、收到推送通知、服务器响应。在移动应用中,这通常对应ViewModel方法的调用或UI元素的点击。
Then块描述预期的状态变化:屏幕切换、API调用、数据库更新。Then中的检查必须可测量且明确 — 它们将成为可执行代码中的断言。
BDD和TDD经常被混淆,尽管它们是不同层次的技术。TDD是代码级别的设计技术:「如何编写实现」。BDD是需求级别的规范技术:「系统应该做什么」。
| 标准 | TDD | BDD |
|---|---|---|
| 焦点 | API设计 | 系统行为 |
| 语言 | 代码(JUnit、XCTest) | 自然语言(Gherkin) |
| 受众 | 开发人员 | 整个团队+客户 |
| 级别 | 单元测试 | 验收/集成测试 |
| 结果 | 代码覆盖的API | 可执行的规范 |
最优秀的移动项目在单个类级别(领域层)使用TDD,在场景级别(功能层)使用BDD。这提供了双重覆盖:TDD保证实现的正确性,BDD保证需求理解的正确性。Google在内部实践中将TDD和BDD结合用于Android应用,如Android Testing文档(2024)所述。
BDD生态系统包括适用于所有主流移动开发平台和语言的框架。工具的选择取决于技术栈和自动化水平。
Cucumber是使用Gherkin场景的最流行的BDD框架。Android项目使用io.cucumber:cucumber-android库,与Espresso和Compose Test等UI测试工具集成。Cucumber支持Kotlin和Java,是同时使用两种语言的工作室的通用选择。
SpecFlow是用于.NET生态系统的BDD框架,用于Xamarin.Forms和.NET MAUI项目。SpecFlow与NUnit和xUnit集成,Step definitions用C#编写。对于移动项目,SpecFlow允许在Android和iOS版本的应用程序之间共享场景。
Swift的iOS开发使用BDD框架Quick和Nimble。Quick提供describe/it风格的DSL来描述场景,Nimble提供具有可读语法的匹配器。这些框架不直接使用Gherkin,但实现了BDD原则:用团队都能理解的语言描述行为。
来看Android项目中BDD的完整示例:订单结算场景。首先编写Gherkin场景,然后用Kotlin编写Step definitions。
BDD方法基于three amigos会议 — 三种角色:开发人员、测试人员和分析师。他们在开发开始前共同编写场景,确定对需求的共同理解。如果三位参与者中任何一位无法通过场景,则说明需求表述不明确。这种做法在「Discovery: Explore Behaviour Using Examples」(Gáspár & North,2021)中有描述,是成熟团队BDD过程的必要组成部分。
Feature: Order Checkout
Scenario: Apply promo code to cart
Given the user has items in the cart
And the total amount is $100
When they apply promo code "WELCOME10"
Then the discount should be $10
And the final total should be $90
Step definitions是将Gherkin场景与测试实现关联起来的代码。每个步骤都是一个带有与Gherkin关键字对应注解的方法。
class CheckoutSteps {
private val cart = Cart()
private val checkout = CheckoutUseCase()
fun `user has items in the cart`() {
cart.addItem(Item("Phone", 100.0))
}
fun `apply promo code`(code: String) {
checkout.applyPromo(cart, code)
}
fun `discount should be`(expected: Double) {
Assertions.assertEquals(expected, checkout.getDiscount())
}
fun `final total should be`(expected: Double) {
Assertions.assertEquals(expected, checkout.getTotal())
}
}
在Android项目中运行BDD测试使用CucumberAndroidJUnitRunner。它扫描资源中的.feature文件,通过正则表达式找到对应的Step definitions,并作为常规instrumentation测试执行场景。结果格式化为客户可理解的HTML报告。
// build.gradle.kts
dependencies {
androidTestImplementation("io.cucumber:cucumber-android:7.18.0")
androidTestImplementation("io.cucumber:cucumber-junit:7.18.0")
}
// CucumberOptions注解
@RunWith(Cucumber::class)
@CucumberOptions(features = "features", glue = ["com.app.steps"])
class CucumberTestRunner
将BDD引入移动开发面临一些实际困难。认识这些问题有助于团队避免失望,建立可持续的BDD过程。
主要问题是Gherkin场景与生产代码的不同步。如果开发人员更改了API但没有更新Step definitions,.feature文件就会与实现不一致。解决方案是在CI/CD流水线中运行BDD测试,并要求合并请求通过测试。“BDD作为门控机制”的做法在Cucumber文档(2024)中有描述,是行业标准。
Cucumber的BDD场景通过Android设备或模拟器上的instrumentation测试执行。这比JVM上的常规单元测试慢10到50倍。大型Android应用程序的单次验收测试可能需要20到30分钟。建议将BDD测试分离到单独的CI作业中并在夜间运行,而单元测试则在每次推送时运行。这种策略平衡了反馈速度和场景覆盖率。
过渡到BDD不仅需要培训开发人员,还需要培训分析师和测试人员。Gherkin是一种简单的语言,但编写好的场景需要练习。新手的典型错误包括:场景过长(超过10步)、Given-When-Then混用、在业务场景中使用技术术语。根据BDD Academy(2024),团队平均需要4到6个sprint才能熟练掌握BDD场景编写。
常见问题
TDD专注于通过单元测试设计API,而BDD专注于通过自然语言场景描述系统行为。BDD扩展了TDD,为包括非技术参与者的整个团队增加了通用语言。
移动开发的主要BDD框架:Cucumber(Android、iOS)、SpecFlow(Xamarin、.NET MAUI)和Quick/Nimble(iOS、Swift)。Cucumber是支持所有主流平台的最通用的选择。
Gherkin是BDD的主要语言,但不是唯一的。iOS框架Quick使用Swift自己的DSL。但建议学习Gherkin,因为它是跨平台项目的事实标准。
BDD将文本规范替换为可执行的场景。客户可以在开发开始前检查场景,在实现后看到通过的绿色报告。这缩短了反馈周期,减少了需求中的错误数量。
可以。BDD是一种方法,而不是工具。BDD的原则可以通过任何测试框架实现,只需以「当条件X时应该做Y」的风格命名测试。但Cucumber和Gherkin为整个团队提供了一致的语言。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。