为什么 Rust 上的 BDD 不是枯燥的测试
\n\n想象一下:您正在用 Rust 编写一个高负载服务。代码安全、快速,但当产品交付给客户时,客户却说:“我以为按钮应该是另一种工作方式。” 熟悉吗?这是代码与期望之间的典型鸿沟。正是在这里,BDD(行为驱动开发) 方法论应运而生,它将业务需求转化为经理和 Rust 编译器都能理解的语言。
\n\n在本文中,我们将探讨如何在实际中实践Rust 上的 BDD。您将学习如何用人类语言(Gherkin)编写场景,使用 cucumber 和 specs 自动化它们,以及为什么这能让您的代码不仅可靠,而且对整个团队透明。没有废话——只有活代码和真实示例。
什么是 BDD,为什么 Rust 是它的理想选择
\n\n行为驱动开发是 TDD 的演进。如果说 TDD 关注的是“如何测试代码”,那么 BDD 回答的是“我们希望看到什么行为”。您用 Gherkin 语言(Given-When-Then)编写场景,这些场景既是测试,也是文档。
\n\nRust 是一种热爱严谨性和可预测性的语言。Rust 中的 BDD 提供了:
\n- \n
- 自文档化代码——任何开发人员阅读场景就能理解业务逻辑。 \n
- 类型级别的安全性——编译器检查您的步骤是否符合结构。 \n
- 并行执行——得益于
async和多线程,Rust 中的 BDD 测试运行迅速。 \n
什么时候 Rust 上的 BDD 特别有用?
\n- \n
- 具有明确契约的 API 服务(REST、GraphQL)。 \n
- 具有业务规则的系统(金融、物流、游戏)。 \n
- 微服务,其中组件间的集成至关重要。 \n
Rust 中的 BDD 工具:Cucumber 和 Specs
\n\nRust 生态系统提供了两种主要方法:
\n\n1. cucumber——经典 BDD 框架
\n允许您用 Gherkin 编写场景(.feature 文件)并将它们与 Rust 代码绑定。支持 async、自定义宏和报告。
2. specs——轻量且快速的替代方案
\n不需要 Gherkin 文件——场景直接用 Rust 通过宏编写。如果您不想引入单独的解析器,这是理想选择。
\n\n我们将重点介绍 cucumber,因为它提供了最大的灵活性并符合 BDD 标准。
\n\n编写第一个 Rust BDD 场景
\n\n让我们创建一个简单的折扣计算器。业务规则:如果购买金额超过 1000 卢布,折扣为 10%。否则为 0%。
\n\n步骤 1. 安装依赖
\n在 Cargo.toml 中添加:
[dev-dependencies]\ncucumber = "0.20"\ntokio = { version = "1", features = ["full"] }\nasync-trait = "0.1"\n\n\n步骤 2. 创建 feature 文件
\n文件 tests/features/discount.feature:
Feature: 折扣计算\n As a 顾客\n I want 在大额购买时获得折扣\n So that 省钱\n\n Scenario: 购买金额超过 1000 卢布\n Given 购买金额等于 "1500"\n When 我请求计算折扣\n Then 折扣金额应为 "150"\n\n Scenario: 购买金额低于 1000 卢布\n Given 购买金额等于 "500"\n When 我请求计算折扣\n Then 折扣金额应为 "0"\n\n\n步骤 3. 用 Rust 编写步骤
\n创建 tests/steps/discount_steps.rs:
use cucumber::{given, then, when, World};\nuse async_trait::async_trait;\n\n#[derive(Debug, Default)]\nstruct DiscountWorld {\n purchase_amount: f64,\n discount: f64,\n}\n\n#[async_trait(?Send)]\nimpl World for DiscountWorld {\n // 为简化起见,空实现\n async fn new() -> Self {\n Self::d