掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

TDD(测试驱动开发)是如何让程序开发告别不可预测的bug的

红灯、绿灯、重构——测试驱动开发的三个步骤


我刚刚开始代码的时候,我从来不写任何测试。我就是觉得自己的代码里没有任何bug。我还觉得,当我对代码的某些地方做出改变、或是加入了一些新功能之后,所有代码依然可以继续运行。

后来我才知道,我太天真了。

虽然程序的功能没有出现什么大问题,但是奇怪的bug会突然出现。而且随着代码体积的增加,出问题的次数会越来越多。

不久之后,每当我需要添加新代码的时候,我就会陷入紧张和恐慌,总觉得新代码会让整个应用瘫痪。

 

就在这个时候,我接触到了测试驱动开发(TDD)。

TDD这种工作方式,能让你在给程序添加新功能的时候更有信心。它会让你感觉,添加新功能后,你的程序不会被破坏。

在开始使用TDD之前,你要先了解以下三个简单的原则:

  • 在写生产代码之前,你要先写一个失败测试
  • 每次只写一个测试,在继续之前确保它失败
  • 在让当前的失败测试通过之前,生产代码的数量不要超过所需求的最小数量

 之后,你就可以重构之前写的生产代码了。

 这个过程可以被理解为红灯(失败测试)->绿灯(通过测试)->重构

 好处


 通过遵循这些原则,TDD能帮你:

  • 调试。想象一下你以前的做法,在做项目的时候,你总会发现有一些模块被撕成碎片,起到在项目截止日之前能解决这些问题。
  • 信心。如果你有着漂亮的设计和架构,但是没有测试,你依然不敢改变代码。而如果你有了各种各样的测试,你就可以放心的去重构代码了。
  • 记录。单元测试就像是代码样本。当你想知道如何调用方法的时候,测试能够很方便的为你提供帮助,告诉你这个方法所有的调用方式。
  • 设计。从定义上看,每一个模块都是可以被测试的。换句话说,每一个模块都是可以割裂的。要想首先写测试,你需要将需要测试的单元从系统中割裂出来。这种工作方式是非常珍贵的。

未测试设定架构


每一个测试都应该遵循下面这样的架构:

Setup模拟一个函数,或是在数据库中添加一些row

Execute调用你想要测试的方法

Assert: 验证你的结果是正确的

Teardown: 清理修改后的数据库记录或是被模拟的对象

在工作中使用TDD


我们用一个例子来讲解TDD的实际使用。

我们将会做一个在字符串中探测某种提及格式的函数,并且用一个用户名来替换他。提及格式是这个样子的:

 @(userId)

现在,我们来写一个测试。在这个例子中,我会使用JavaScript Tape测试框架,因为它的使用相对简单一些:

 const test = require('tape')

const parseMentionableText = require(‘./parse-mentionable-text');

test('should replace mentions with user objects', assert => {

  const text = 'Hello there, @(2) and @(104)!';

  const users = [

    { id: '2', name: 'John Doe' },

    { id: '104', name: 'Foo Baz' },

  ];

  const actual = parseMentionableText(text, users);

  const expected = 'Hello there, John Doe and Foo Baz!’;

  assert.equal(actual, expected);

  assert.end();

})

我创建了一个名叫parse-mentionable-text.js的文件,它的return“OK”

红灯


现在来运行测试,看看它是否会失败。


好的。当测试失败之后,我们开始这个功能之后,它并没有起作用。

这是非常重要的一部。在体积较大的代码库中,你有的时候可以针对一个逻辑来写测试,但是由于一些副作用的存在,一些原本应该失败的测试会显示通过。这意味着,你需要重新设计你的测试。记住——在进行测试的时候,刚开始一定要失败。

绿灯


在测试失败之后,我们必须要确保用最少的代码让测试通过:

 const { find } = require('lodash');

module.exports = (text, users) => {

  const mentionRegex = /@\([0-9]+\)/g;

  const stripMentionRegex = /[@()]+/g;

  return text.replace(mentionRegex, (match) => {

    const mentionId = match.replace(stripMentionRegex, ‘');

    return find(users, { id: mentionId }).name;

  });

} 

要想检查我们的实施是否符合要求,我们必须要再运行一次测试:

 

重构


好的!一切都按照我们预想的那样工作了。现在,我们需要清理代码,让它变得可读性更高:

 const { find, curry } = require('lodash');

const stripMentionFormat = (mention) => mention.replace(/[@()]+/g, '');

const replaceMentionWithUsername = (users, mentionFormat) => {

  const mentionId = stripMentionFormat(mentionFormat);

  return find(users, { id: mentionId }).name;

}

module.exports = (text, users) => {

  const mentionRegex = /@\([0-9]+\)/g;

  const _replaceMentionWithUsername = curry(replaceMentionWithUsername);

  return text.replace(mentionRegex, _replaceMentionWithUsername(users));

}

最好的部分,是在重构了代码之后,你可以进行检查,通过再次运行测试,来看看功能是否符合你的功能。

写代码前先写测试,这种工作方式会彻底改变你写代码的方式。在添加新代码的时候,这种方式能让你更自信。你不再害怕会破坏程序,并让你的工作速度更快。

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

  • 营运车判定查询

    输入车牌号码或车架号,判定是否属于营运车辆。

    输入车牌号码或车架号,判定是否属于营运车辆。

  • 名下车辆数量查询

    根据身份证号码/统一社会信用代码查询名下车辆数量。

    根据身份证号码/统一社会信用代码查询名下车辆数量。

  • 车辆理赔情况查询

    根据身份证号码/社会统一信用代码/车架号/车牌号,查询车辆是否有理赔情况。

    根据身份证号码/社会统一信用代码/车架号/车牌号,查询车辆是否有理赔情况。

  • 车辆过户次数查询

    根据身份证号码/社会统一信用代码/车牌号/车架号,查询车辆的过户次数信息。

    根据身份证号码/社会统一信用代码/车牌号/车架号,查询车辆的过户次数信息。

  • 风险人员分值

    根据姓名和身份证查询风险人员分值。

    根据姓名和身份证查询风险人员分值。

0512-88869195
数 据 驱 动 未 来
Data Drives The Future