我一直在学习使用specflow为BDD(行为驱动开发)编写测试用例。如果我用BDD编写综合测试,是否有必要单独编写TDD(测试驱动开发)测试?是否有必要分别为TDD和BDD编写测试用例,或者它们实际上是同一件事吗?

在我看来两者都是相同的,唯一的区别是BDD测试用例可以被非开发人员和测试人员理解。

评论

是一个关键的区别,是的。

BDD =行为驱动开发,其他任何人都想知道。 TDD =测试驱动开发,以确保完整性。

将BDD与TDD混淆,就像将宏观经济学与微观经济学混淆。他们是不同的。 BDD =使用示例建立对需求的理解,并且可以选择用来驱动自动Macro测试。 (agilenoir.biz/zh/am-i-behavioral-or-not),TDD =编写微型测试以驱动编写代码。敏捷思想播客也涵盖了这些差异:agilenoir.biz/zh/agilethoughts/test-automation-pyramid-series

#1 楼

BDD和TDD之间的区别是BDD以B开头,而TDD以T开头。但是,严重的是,TDD的陷阱在于,太多的开发人员在编写单元测试时专注于“如何”,所以最终脆弱的测试仅能确认系统是否已完成工作。

BDD提供了新的词汇表,因此成为编写单元测试的重点。基本上,这是功能驱动的TDD方法。

评论


究竟。 BDD和TDD之间没有区别。 BDD是正确的TDD。正确的TDD是BDD。问题在于,正确执行TDD很难,或者更确切地说,学习正确执行TDD很难。原因是TDD与测试绝对无关,但是当所有术语都与测试有关时,很难理解这一重要事实。因此,BDD从字面上看只是TDD,所有测试术语都被行为示例术语所取代。就像“尽量不要想到粉红色的大象”一样。

–Jörg W Mittag
2012年2月16日在3:40

我喜欢“尽量不要想到粉红色的大象”的示例,因为一旦您告诉某人这样做,便可以想到LOL。

–迈克尔·布朗(Michael Brown)
13年2月28日在16:13

同意TDD / BDD / xDD的麻烦在于,无论您采用哪种方法,都会使其他方法显得有些陌生,关键是不要太担心它,而应使用最适合您情况的方法。一种非常普遍的方法是为BA使用BDD类型方法来定义您的需求(您的故事中给出/何时/然后),但是开发人员本身并不使用BDD框架。他们只是转换要求。在标准的xUnit类型框架中编写和编写单元/集成/验收测试时,采用TDD类型的方法。

–克里斯·李(Chris Lee)
2014年1月15日在22:32



没什么不同,一个是另一个的子集,但是有一个区别,就是您的接受标准变成了可以驱动较低级单元测试的可执行文件...因此,在中间有一点抽象,您必须考虑在代码中,是指在较低级别的测试通过之后需要调用SUT代码的规范(规范),该规范应该通过。因此,规范是BDD中引入的规范,因此您不能说具体没有区别。确保您在两个版本中都使用较低级别的单元测试来测试代码,但是在顶层还增加了一层规范

–WeDoTDD.com
15年8月7日在6:42

TDD和BDD有很大的不同。第一个仅用于由软件开发人员使用/完成,而第二个则用于使开发人员和“客户”(最终用户,业务专家,业务分析师或其他不读取/编写编程代码的项目利益相关者)积极参与)创建测试。 TDD不仅适用于“单元”测试,而且适用于集成,功能或端到端测试;只要测试仅由开发人员读写,那就是TDD;否则是BDD。

–罗杰里奥
18年5月16日在21:10

#2 楼

行为驱动开发是对测试驱动开发的扩展/修订。其目的是帮助设计系统的人员(即开发人员)识别要编写的适当测试-即反映利益相关者所需行为的测试。效果最终是相同的-开发测试,然后开发通过测试的代码/系统。 BDD希望该测试实际上对表明系统符合要求很有用。

UPDATE

代码单元(单个方法)可能过于精细而无法表示行为测试所代表的行为,但是您仍然应该使用单元测试来测试它们,以确保它们正常运行。如果这是“ TDD”测试的意思,那么是的,您仍然需要它们。

评论


大型和复杂的项目呢?将BDD和TDD方面分解为各自的项目并分别进行测试是否更好?

–arjun
2012-02-15 17:26

行为本身可以分解为详细的测试用例,并在BDD中进行测试。当人们谈论拆分BDD和TDD方面时,我感到困惑

–arjun
2012-02-15 17:32

没错-单元测试仍然可以测试方法的行为。我认为造成这种混乱的原因可能是因为可能很难直接将方法的行为追溯到外部利益相关者要求和/或理解的行为。

–马修·弗林(Matthew Flynn)
2012-02-15 17:36



至于将这两个项目划分为不同的项目,这取决于“技术”单元测试的存在是否会使需要审查测试的“行为”映射到需求的人们感到困惑。如果将它们分开很有用,请继续。否则,我不会打扰。

–马修·弗林(Matthew Flynn)
2012-2-15在17:41

行为和代码之间的@MatthewFlynn可追溯性绝对是问题所在,为此您需要了解设计(利益相关者可能不具备的能力)

– jk。
2012-2-15在19:08

#3 楼

BDD使用了一种称为“无处不在的语言”的东西,这是开发人员和客户都可以理解的知识体系。这种无处不在的语言用于在客户理解的水平上塑造和开发所需的需求和测试。

在BDD规定的需求和测试范围内,您将使用“普通” TDD开发软件。这样创建的单元测试将作为实现代码的测试套件,而BDD测试或多或少地充当客户的验收测试。

#4 楼

以我的经验,TDD的最大问题是“ T”。它使外行人员(经理,测试人员,非TDD开发人员)在头脑中将其等同于瀑布式的传统后期开发“测试”阶段。这是任何人都可以解决的问题。

许多人都在困扰的问题是TDD是针对开发人员的,而不是测试人员的。正确的TDD并不是主要的测试策略或验收测试工具,而是一种从头开始推动良好软件设计的技术-小型,松散耦合的类,清晰,定义明确的接口以及通过持续的重构不断清理的代码。从置信位置常规,频繁地执行重构。

您碰巧最终得到了可以构成CI /构建过程一部分的综合测试套件,这是一个奖励,而不是目标。

BDD通过桥接来称赞这一点。业务需求和更高级别的验收测试之间的差距。 BDD套件的令人满意之处在于它可以扩展开发过程,并确定何时可以适当交付整个产品。

评论


对于他们来说,退出之前将其缩至首字母缩写词的第二个字母将是很好的。 “开车。”如果您一直在追踪,很难开车。

– tottinge
15年5月22日在16:56

#5 楼

TDD和BDD之间的差异很细微,并且主要归结为语言。 BDD测试通常以以下形式编写:

public void shouldBuyBread() throws Exception {
   //given  
   given(seller.askForBread()).willReturn(new Bread());

   //when
   Goods goods = shop.buyBread();

   //then
   assertThat(goods, containBread());
 }  


如果按照行为来构建测试框架,则有助于确定类的职责并导致更好的设计(至少根据BDD'ers的说法)。 BDD有时专注于您的域专家/客户可以理解的可执行规范。

BDD还与Martin Fowler所谓的“外部-内部”或“模拟”测试相关,而不是基于状态的验证。

评论


这不仅仅是语言。...代码中也有“规范”,在单元测试的顶层。

–WeDoTDD.com
15年8月7日在6:43

#6 楼

由于我的上次回复不太成功,因此我将尝试一种非常简单的方法。



Behaviour Driven DevelopmentTest Driven Development的子集


TDD专注于针对每个功能的每个单元测试,不管它做什么。 BDD专注于重要的软件


IdiomTDD准备测试,BDD强制采用讲故事的格式

JavaScript示例

茉莉中的单元测试(BDD

describe("A suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
  });
});


jsUnity中的单元测试(TDD

function test_lists() { assert.areEqual([1, 2, 3], [1, 2, 3]) }


以下是一些Python库,可帮助创建更多的BDD,例如使用unittest框架的测试:



莴苣:适用于python的黄瓜
HamCrest


#7 楼

BDD为测试增加了一层抽象。较高级别的代码(通常在txt中)描述了系统测试的内容,较低级别的代码描述了系统对其进行测试的方式。因此,BDD框架可以在较低级别的代码中使用TDD框架。

通过保持DRY可以大有帮助。通过TDD,您可以轻松地获得包含大量代码重复的“湿”测试,这使它们很容易通过重构代码和带有代码的测试而被破坏。通过BDD,您只需通过重构代码即可修改较低的抽象级别,因此,如果规范不变,则较高级别的代码也不会更改。

Btw。这是简单的“干净代码”,通常只需阅读较高抽象级别的内容以了解其功能,然后仅在确实需要时才深入研究较低抽象级别的测试代码。这使得(测试)代码总体上更容易理解。

一个不好的例子(因为它太简单了):

茉莉TDD样式

Calculator.add.specs

describe("Calculator: add", function (){

    it("should be able to add 2 numbers together", function (){
        var total = add(1, 2);
        expect(total).toBe(3);
    });

    it("should be able to add 3 numbers together", function (){
        var total = add(1, 2, 3);
        expect(total).toBe(6);
    });
});


茉莉花黄瓜BDD样式

calculator.specs

feature('Calculator: add')
    .scenario('should be able to add 2 numbers together')
        .when('I enter "1"')
        .and('I add "2"')
        .then('I should get "3"')
    .scenario('should be able to add 3 numbers together')
        .when('I enter "1"')
        .and('I add "2"')
        .and('I add "3"')
        .then('I should get "6"')


calculator.steps

featureSteps('Calculator:')
    .before(function(){
        this.values = [];
        this.total = null;
    })
    .when('I enter "(.*)"', function(value){
        this.values.push(Number(value));
    })
    .when('I add "(.*)"', function(value){
        this.values.push(Number(value));
    })
    .then('I should get "(.*)"', function(expectedTotal){
        this.total = add.apply(null, this.values);
        expect(this.total).toBe(Number(expectedTotal));
    });


实现


function add(){
    var args = Array.prototype.slice.call(arguments);
    var total = 0;
    for (var i in args)
        total += args[i];
    return total;
}


现在,如果我将add()函数重命名为sum(),则必须在2个地方更改TDD代码,而在步骤文件中仅在单个位置更改BDD代码。 Ofc。添加更多抽象级别需要更多代码...

#8 楼

只是为了使其混淆,请意识到现在有很多人将BDD与黄瓜相关联。他们将您使用小黄瓜派生的语法编写的任何测试视为BDD检查,而将使用单元测试框架(junit,unittest.py等)编写的任何测试视为TDD。

这是错误的。在这种情况下,媒体没有定义消息。也不使用模拟(IMHO)。

已经有了主要的区别:BDD是一种功能驱动的技术,使用了无处不在的语言和由外而内的方法。我们为一个故事或一个故事的一部分编写规范(以后将作为测试运行)。细小和具体很重要,这是从故事的这个方面需要/期望的“示例性解释”。

BDD规范通常以类似于小黄瓜的语言编写,如果需要指定功能部件并不能全部读取/写入代码(在大型组织中经常发生,或者团队中有真正的客户)。否则,不需要小黄瓜。这些往往是著名的3次Amigos会议的结果:业务,测试,开发人员全部一起完成这些任务。当使用BDD框架(适合,黄瓜等)时,测试速度较慢,并且需要更多的基础结构,因此我们保持这些相对稀疏-每个故事很少。

创建代码时使用TDD 。目的是使功能的编写有序进行,并提供大量重构的机会,并有合理的把握,确保您在编写新代码时不会意外破坏任何内容。它不需要描述系统的功能,只需描述一种方法(或少数具有共同作用的方法)即可。除开发人员外,任何人都不需要阅读这里编写的测试,但是他们必须快速运行并隔离错误。因为每个类成员函数(“方法”)可能有很多测试,所以它们运行得很快。

如果在外层执行BDD,则在内部循环中执行TDD仍然很有用,因为它具有重构功能,并且能够更快地发现并避免错误。

大型系统的正确性也不能证明。都不能替代笔和性能测试。两者都不能确保线程安全。这些测试有助于开发,不能保证完美的防错性。