如何解读TDD?

测试驱动开发是一种软件开发实践,源于1999年Kent Beck《Extreme Programming Explained》一书中的测试先行这一概念。Kent Beck在2003年再次提到 – TDD鼓励简单的设计并激发信心。经过后期的发展,TDD已经成长为一门独立的软件开发技术,其名气甚至盖过了极限编程。

image

TDD采用了一种以终为始的思维方式,它依赖于非常短的开发周期的重复:先将需求转换为非常具体的测试用例,然后改进代码,从而让测试通过。实际编码过程体现为:在开发业务功能代码之前,先编写测试代码。测试代码确定了我们要验收什么以及如何验收,然后再去编写功能代码,当测试通过时,代表功能完成。

在实践TDD的过程中,人们对TDD形成了三层理解:

  • Task-Driven Development,任务驱动开发。
  • Test-Driven Development,测试驱动开发。
  • Test-Driven Design,测试驱动设计。

在我的TDD训练营中,我将第一条的重要性提高到了首位。

Task-Driven Development

任务拆解是TDD的一个关键的步骤。在开始编写进入代码环节之前,需要对业务需求做拆分,这个过程我们把它称之为Tasking。比如,一个用户登录的业务需求,我们大体可以拆分成如下几个Task:

  1. 假定用户名不存在时,当用户登录,则登录失败
  2. 假定用户名正确且密码错误时,当用户登录,则登录失败
  3. 假定用户名和密码都正确时,当用户登录,登录成功

通过上面的三个Tasking,可以总结出一个模式:Given When Then:

  1. Given 用户名不存在,When 用户登录, Then 登录失败
  2. Given 用户名正确且密码错误,When 用户登录, Then 登录失败
  3. Given 用户名和密码都正确,When 用户登录, Then 登录成功

Given When Then借鉴了BBD(Behavior Driven Development)里面的模式,它更加关注用户如何使用系统,即系统所提供的功能,从理解上更偏向于业务语言。对于一些技术人员,在初学阶段,需要多去练习,体会不同点,可以选业务人员视角索要一些反馈。

在TDD中,我将之称为Tasking三步曲:

  • Given,代表特定的业务场景
  • When,代表用户发生的行为
  • Then,代表行为产生的结果

Test-Driven Development

上一步Tasking拆分好的一系列Task,经过沟通澄清之后,就可以将这些Task翻译成测试。在翻译的过程中需要注意的一个核心点是 – 保持业务概念的统一(统一语言的应用)。如何理解这一点呢,来个看一个Task:

Given 一个有空位的停车场,When 停车,Then 停车成功,返回一张票据

翻译成测试代码后:

@Test
void should_return_ticket_when_parking_given_a_parking_lot_has_available_space(){
    ParkingLot parkinglot = new ParkingLot(1);
    String car = "布加迪"
    String ticket = parkinglot.park(car);
    assertNotNull(ticket)
}

在上述测试代码中,car和ticket都是用了字符串来表示(基本类型偏执),车和票据这两个业务概念没有体现出来,建议是使用CarTicket来代替这两个业务概念。

极限的TDD,会将以终为始的思维运用到极致。它提倡在写测试用例的时候,先写断言,然后一步一步往前倒逼驱动出来测试代码,比如上述的例子,经历的过程如下:

{
    // 1\. 我最终要验收的是停车票,代表停车成功
    assertNotNull(ticket);
}

{
    // 2\. 停车票是停车后返回的
    Ticker ticket = parkinglot.park(car);
    assertNotNull(ticket)
}

{
    // 3\. 停的车是我新开进来的车
    Car car = new Car();
    Ticker ticket = parkinglot.park(car);
    assertNotNull(ticket)
}

@Test
void should_return_ticket_when_parking_given_a_parking_lot_has_available_space() {
    // 4\. 停进了有空位的停车场
    ParkingLot parkinglot = new ParkingLot(1);
    Car car = new Car();
    Ticker ticket = parkinglot.park(car);
    assertNotNull(ticket)
}

这种方式在刚开始是很难做到的,但是通过大量刻意练习,形成这种行为习惯,内化成思维习惯,也是益处多多,非常建议你去刻意练习。

Test-Driven Design

TDD并不会驱动出好的设计,TDD只会给你及时的反馈什么可能是糟糕的设计 – Kent Beck

关于这点,请阅读测试如何驱动设计?

你可能感兴趣的:(如何解读TDD?)