ATDD案例

wen java案例 2

本文目录导读:

ATDD案例

  1. 背景与角色设定
  2. 业务需求(原始描述)
  3. ATDD第一步:需求澄清(三方一起讨论)
  4. ATDD第二步:定义验收标准(行为驱动语言 Gherkin格式)
  5. ATDD第三步:将验收标准自动化
  6. ATDD第四步:运行测试 -> 全部失败(红灯)
  7. ATDD第五步:开发实现代码
  8. ATDD第六步:再次运行测试 -> 全部通过(绿灯)
  9. ATDD第七步:重构(保持测试通过)
  10. 扩展:当需求变更时
  11. 十一、ATDD vs TDD 对比
  12. ATDD的标准流程
  13. 十三、ATDD 工具链推荐

下面给你一个完整、可执行的ATDD(Acceptance Test-Driven Development,验收测试驱动开发)案例

我会以 “用户登录功能” 为例,从业务需求需求澄清,再到编写验收测试,最后到代码实现,完整走一遍ATDD流程。


背景与角色设定

角色 人物 关注点
业务方(PO) 王总 业务规则、用户体验
开发 小李 代码实现、技术可行性
测试 小张 验收标准、测试用例

业务需求(原始描述)

王总说:“我们要做一个登录功能,用户输入用户名和密码就能登录,如果密码错了要提示错误,登录成功后要跳转到首页。”


ATDD第一步:需求澄清(三方一起讨论)

在写代码之前,三方坐在一起,把需求问清楚。

典型对话:

  • 测试小张:密码错误提示什么?密码连续错几次要锁定吗?
  • 业务王总:提示“用户名或密码错误”,不要告诉用户是哪个错了(防止黑客试探),密码错5次锁定账号10分钟。
  • 开发小李:用户名是邮箱还是手机号?有没有验证码?
  • 业务王总:目前支持用邮箱登录,验证码先不做,MVP阶段先不做。

ATDD第二步:定义验收标准(行为驱动语言 Gherkin格式)

经过澄清,测试小张把需求转化为可执行的验收标准(Gherkin格式 —— Given-When-Then):

Feature: 用户登录
  Scenario: 正确凭据登录成功
    Given 用户输入正确的用户名 "zhangsan@example.com"
    And 用户输入正确的密码 "abc123"
    When 用户点击登录按钮
    Then 系统跳转到首页
    And 系统显示登录成功提示
  Scenario: 密码错误登录失败
    Given 用户输入正确的用户名 "zhangsan@example.com"
    And 用户输入错误的密码 "wrongpass"
    When 用户点击登录按钮
    Then 系统显示"用户名或密码错误"
    And 系统停留在登录页
  Scenario: 连续5次密码错误锁定账号
    Given 用户输入正确的用户名 "zhangsan@example.com"
    And 用户输入错误的密码 "wrongpass"
    When 用户连续尝试登录5次失败
    Then 系统提示"账号已被锁定,请10分钟后再试"
    And 锁定期间即使输入正确密码也登录失败
  Scenario: 用户名不存在
    Given 用户输入不存在的用户名 "ghost@example.com"
    And 用户输入任意密码 "abc123"
    When 用户点击登录按钮
    Then 系统显示"用户名或密码错误"

✅ 写好了这个文件之后,这就是团队和业务的“契约”,只有这个文件里的场景全部通过,功能才算完成。


ATDD第三步:将验收标准自动化

测试小张使用 Cucumber(BDD框架) 将这些Gherkin场景转化为自动化测试代码。

public class LoginSteps {
    private LoginPage loginPage;
    private String actualMessage;
    @Given("用户输入正确的用户名 {string}")
    public void enterCorrectUsername(String username) {
        loginPage.enterUsername(username);
    }
    @Given("用户输入正确的密码 {string}")
    public void enterCorrectPassword(String password) {
        loginPage.enterPassword(password);
    }
    @When("用户点击登录按钮")
    public void clickLoginButton() {
        actualMessage = loginPage.clickLogin();
    }
    @Then("系统跳转到首页")
    public void verifyRedirectToHome() {
        assertEquals("homepage", loginPage.getCurrentPage());
    }
    @Then("系统显示{string}")
    public void verifyMessage(String expectedMsg) {
        assertEquals(expectedMsg, actualMessage);
    }
    // ... 其他步骤实现
}

ATDD第四步:运行测试 -> 全部失败(红灯)

代码还没有实现,或者只写了空壳类。

运行测试结果:

1) Scenario: 正确凭据登录成功 - FAILED
2) Scenario: 密码错误登录失败 - FAILED
3) Scenario: 连续5次密码错误锁定账号 - FAILED
4) Scenario: 用户名不存在 - FAILED

红灯是正常的,这符合 TDD/ATDD的“先失败后成功”原则


ATDD第五步:开发实现代码

开发小李现在根据 验收测试 来写实现代码,目标是让测试全部变绿。

public class LoginService {
    private static final int MAX_ATTEMPTS = 5;
    private static final long LOCK_DURATION_MS = 10 * 60 * 1000; // 10分钟
    private Map<String, Account> accounts = new HashMap<>();
    private Map<String, LoginAttempt> attemptHistory = new HashMap<>();
    public LoginResult login(String username, String password) {
        // 1. 检查是否被锁定
        LoginAttempt attempt = attemptHistory.getOrDefault(username, new LoginAttempt());
        if (attempt.isLocked()) {
            return new LoginResult(false, "账号已被锁定,请10分钟后再试");
        }
        // 2. 验证账号存在
        Account account = accounts.get(username);
        if (account == null) {
            recordFailure(attempt, username);
            return new LoginResult(false, "用户名或密码错误");
        }
        // 3. 验证密码
        if (account.getPassword().equals(password)) {
            resetAttempt(username);
            return new LoginResult(true, "登录成功");
        } else {
            recordFailure(attempt, username);
            if (attempt.getCount() >= MAX_ATTEMPTS) {
                attempt.lock();
                return new LoginResult(false, "账号已被锁定,请10分钟后再试");
            }
            return new LoginResult(false, "用户名或密码错误");
        }
    }
    private void recordFailure(LoginAttempt attempt, String username) {
        attempt.incrementCount();
        attemptHistory.put(username, attempt);
    }
    private void resetAttempt(String username) {
        attemptHistory.remove(username);
    }
}

ATDD第六步:再次运行测试 -> 全部通过(绿灯)

1) Scenario: 正确凭据登录成功 - PASSED
2) Scenario: 密码错误登录失败 - PASSED
3) Scenario: 连续5次密码错误锁定账号 - PASSED
4) Scenario: 用户名不存在 - PASSED

✅ 绿了,功能就完成了!


ATDD第七步:重构(保持测试通过)

开发小李对代码进行重构:

  • 将常量提取到配置文件
  • 增加日志记录
  • 优化代码结构

重构后再次运行测试,确保依然是绿灯。


扩展:当需求变更时

假设王总过来说:“密码连续错误5次,锁定时间改为30分钟。”

你不能直接改代码!

你需要这样做:

  1. 先改验收测试文件(Gherkin)

    Given 用户输入正确的用户名 "zhangsan@example.com"
    And 用户输入错误的密码 "wrongpass"
    When 用户连续尝试登录5次失败
    Then 系统提示"账号已被锁定,请30分钟后再试"
  2. 运行测试 -> 会有一个测试失败(因为代码还是10分钟)

  3. 修改实现代码,将锁定时间改为30分钟

  4. 再次运行测试 -> 全部通过

这就是ATDD的核心优势:验收测试先行,需求变更也由测试驱动


十一、ATDD vs TDD 对比

维度 TDD ATDD
测试对象 单元测试(函数/方法) 验收测试(系统行为)
参与者 开发 开发 + 测试 + 业务
粒度 代码级 业务/需求级
测试语言 代码(JUnit等) 自然语言(Gherkin)
目的 保证代码质量 保证需求被正确实现

ATDD的标准流程

① 三方讨论需求
      ↓
② 写出Gherkin验收标准
      ↓
③ 自动化验收测试
      ↓
④ 运行测试失败(红灯)
      ↓
⑤ 开发写实现代码
      ↓
⑥ 测试通过(绿灯)
      ↓
⑦ 重构
      ↓
⑧ 持续回归(需求变更时重复③-⑥)

十三、ATDD 工具链推荐

工具 用途
Cucumber / SpecFlow Gherkin语言解析、测试执行
JUnit / NUnit 底层断言
Selenium / Playwright UI自动化
RestAssured API自动化验收
Jira(Xray) 验收测试管理

如果你告诉我你的实际业务场景(比如订单、支付、注册、库存管理),我可以帮你写一套针对那个场景的完整ATDD案例(包括完整的Gherkin文件和测试代码模板)。

上一篇BDD案例

下一篇TDD案例

抱歉,评论功能暂时关闭!