前言

如果你关注软件开发最佳实践方面的话题,你肯定听说过测试驱动开发(TDD - Test Driven Development) 和行为驱动开发(BDD - Behavior Driven Development)。这篇文章会为你阐述这两种模式的含义并举例,同时对二者进行比较。

测试驱动开发 (TDD)

当我第一次听到TDD这个词,从字面上理解,觉得很简单,就是用于驱动软件开发的测试。
这没有错,如果对TDD作进一步的解释,这个过程可以进一步分解为5个步骤:

  1. 首先,开发者在码业务前写一些测试用例
  2. 运行这些测试用例。结果肯定是运行失败,因为测试用例中的业务逻辑还没实现嘛
  3. 开发者实现测试用例中的业务逻辑
  4. 再运行测试用例, 如果开发者代码能力不错,这些测试用例应该可以跑通了(pass)
  5. 对业务代码及时重构,包括增加注释,清理重复等。因为没人比开发者自己更了解哪些代码会对哪些部分造成影响从而导致测试失败(fail)

当需要开发新需求新功能时,重复上述步骤。流程如下图所示:
TDD与BDD的区别说明 - 图1

TDD举例

我们通过举例来了解一下如何实践TDD。例子中的代码可以从github上获取tdd-vs-bdd。将代码clone下来,执行命令npm install && grunt
假设我们想写一个计算阶乘的函数(这是一个很刻意的例子,但是这个例子对我们指出TDD和BDD的区别很有帮助)。TDD的常用方式是运行某函数,然后断言结果满足某个值
在阶乘的例子中,我们使用的javascript测试框架是Mocha。废话不说,上代码:

  1. var assert = require('assert'),
  2. factorial = require('../index');
  3. suite('Test', function (){
  4. setup(function (){
  5. // Create any objects that we might need
  6. });
  7. suite('#factorial()', function (){
  8. test('equals 1 for sets of zero length', function (){
  9. assert.equal(1, factorial(0));
  10. });
  11. test('equals 1 for sets of length one', function (){
  12. assert.equal(1, factorial(1));
  13. });
  14. test('equals 2 for sets of length two', function (){
  15. assert.equal(2, factorial(2));
  16. });
  17. test('equals 6 for sets of length three', function (){
  18. assert.equal(6, factorial(3));
  19. });
  20. });
  21. });

显然上述测试会失败,因为我们尚未实现函数功能。所以接下来我们需要实现满足上述测试用例的阶乘函数。代码如下:

  1. module.exports = function (n) {
  2. if (n < 0) return NaN;
  3. if (n === 0) return 1;
  4. return n * factorial(n - 1);
  5. };

现在我们再次运行测试用例,所有的case都跑通了! 这就是TDD的使用方式。
我们接着来学习BDD,看它与TDD有什么不同。

行为驱动开发 (BDD)

BDD是什么? 很多人都会感到很模糊。 有人说它与TDD类似,有人说它是对TDD做了扩展。
抛开晦涩的定义,我们只需要记住一点:BDD旨在消除TDD过程中可能造成的问题
与TDD相比,BDD是通过编写行为和规范来驱动软件开发。 行为和规范可能看起来与测试非常相似,但是它们之间却有着微妙但重要的区别。

BDD举例

我们还以讲解TDD中用到的阶乘函数为例:

  1. var assert = require('assert'),
  2. factorial = require('../index');
  3. describe('Test', function (){
  4. before(function(){
  5. // Stuff to do before the tests, like imports, what not
  6. });
  7. describe('#factorial()', function (){
  8. it('should return 1 when given 0', function (){
  9. factorial(0).should.equal(1);
  10. });
  11. it('should return 1 when given 1', function (){
  12. factorial(1).should.equal(1);
  13. });
  14. it('should return 2 when given 2', function (){
  15. factorial(2).should.equal(2);
  16. });
  17. it('should return 6 when given 3', function (){
  18. factorial(3).should.equal(6);
  19. });
  20. });
  21. after(function () {
  22. // Anything after the tests have finished
  23. });
  24. });

看出TDD和BDD的区别了吗?其实就是措辞。BDD的描述采用了更加’繁琐’的描述风格,阅读BDD的测试用例就像是阅读一篇文档。
这正是为什么我说BDD旨在消除TDD过程中可能造成的问题的原因所在。BDD赋予的这种像阅读句子一样阅读测试的能力有助于带来对测试认知上的转变,有助于我们去考虑如何更好写测试。当你可以流畅的阅读自己写的测试,你自然可以写出更好更全面的测试用例。
尽管上面的举例非常简单,当我们可以看出:BDD更注重功能本身而非单纯的测试用例运行结果。这也是我们经常听到的一句关于BDD本质的另外一种表达方式:BDD帮助开发人员设计(design)软件,TDD帮助开发人员测试(test)软件

TDD vs BDD

在TDD和BDD之间做选择不是件容易的事。这取决于开发者使用的语言是否有合适的测试框架,小组的同学们是否适应对应框架的用法等等。
有人总是声称BDD优于TDD,因为BDD有助于消除TDD开发中可能产生的问题(issue)。
BDD可能有助于防止问题,但并不能保证消除所有问题。一些由糟糕的代码结构,不好的编程实践引发的问题不可能通过BDD就可以消除的。只是如果开发者BDD测试写的很糟糕,那基本上也不可能开发出健壮的功能。

结论

代码有风格总比没有强。能写出漂亮TDD测试用例的开发者不会比精通BDD的开发者产出更多的bug。如果你发现你写的TDD测试用例不够充分,但是你还是希望开发出好的软件,那么不妨给BDD一个机会。如果你对TDD和BDD都不熟悉,那么我建议你从TDD开始学起。不论TDD还是BDD,它们的共同好处是强制要求开发者必须写测试用例以提高代码的质量。
我不是研究TDD和BDD的专家。我只是因为不了解TDD和BDD所以自己做了一番调查,这篇文章就是我调查的结果。本文中用到的代码可以从github拿到:tdd-vs-bdd
如果你有任何建议或者发现了文章中错误的地方,又或你仅仅是想表达不同意我的观点,我也很乐意倾听。请与我联系Josh Davis
最后,谢谢您花时间阅读本文 ?

译者按

最近开发组件库时在写单元测试,想着把单测的知识点再回顾一下。正好读到这篇关于TDD和BDD的总结,感觉写的很好,想翻译一下的原因在于:

  1. 像要翻译好一篇文章,除了要有好的英语基础,为了尽可能准确的翻译文章中提到的一些技术点,还需要多查更多文献,使得学习更有目的性。
  2. 这篇文章没有涉及很深的技术,感觉比较好翻译。

有了这个小经历,再看那些优秀流畅的技术译文,才深深觉得作者的不易与用心。哪些可以直译,哪些又必须意译同时还要准确表达作者意图,都需要细细思量。有时一句话可能需要斟酌很久,会有一种我到底是在学习技术还是学习英语文法的奇怪感觉。
总之,收获不单单在技术上。以后有时间继续翻译好文 ?

转载自: https://blog.csdn.net/Napoleonxxx/article/details/88808475