何时使用单元测试与 BDD 测试

When to use a unit test vs a BDD test

基于对 BDD 的一些简单阅读,我得出结论,单元测试是测试应用程序某些细粒度部分的好工具,而 BDD 是更高级别的,您可以在其中练习功能工作流。

我会考虑作为单元测试候选人的一些项目:排序算法、状态缩减器、几何计算等...

我认为 BDD 候选人的项目将是功能工作流程:将项目添加到购物车、登录、搜索网站内容以查找课程 material 等...

我被客户要求写一个排序算法,通常我会写一个单元测试,比如:

public class SorterTest
{
    [TestMethod]
    public void TestSort()
    {
        var numbers = new List<int>() { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
        var expected = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        var sorter = new Sorter()
        {
            Input = numbers
        };

        var sorted = sorter.Sort();
        CollectionAssert.AreEqual(expected, sorted.ToList());
    }
}

但是,客户要求进行BDD测试,所以我想出了以下办法:

Feature: Sort
In order to to ensure sorting of integers
I want to be able to sort a collection of integers

Scenario Outline: Perform a sort
Given I have these integers: '<input>'
When I sort them
Then the result should be '<expected>'

Examples:
| input                           | expected          |
| 9,8,7,6,5,4,3,2,1               | 1,2,3,4,5,6,7,8,9 |

为了使我的 Sorter 对象可用于 BDD 测试,我必须对其进行更改:

    private Sorter sorter = new Sorter();
    [Given(@"^I have the following integers: '(.*)'$")]
    public void GivenIHaveTheFollowingIntegers(string numbers)
    {            
        var inputs = numbers.Split(',')
                            .Select(s => Convert.ToInt32(s.Trim()))
                            .ToList();
        sorter.Input = inputs;
    }

关于使用 Given 设置测试的注意事项,我必须添加一个 Input 属性 到 Sorter tp 准备排序。缺点是在应用程序代码中,如果我希望我的排序器执行排序,我将始终需要在执行排序之前设置此 属性:

sorter.Input = intCollection;
var result = sorter.sort();

我宁愿只拥有:

var result = sorter.sort(intCollection);

BDD 适合这种测试吗?如果是这样,我这样做对吗?添加 Input 属性 感觉不对,我应该以其他方式添加吗?

如果不合适,如何在 BDD 和单元测试之间划清界限?有一个 existing SO post,但答案参考了一本书。如果能得到更好的指导就好了。

我会写一个单元测试。

你提到了

Some of the items I would consider candidates for unit tests: sorting algorithms, state reducers, geometric calculations, etc...

Items I would consider candidates for BDD would be feature workflows: adding an item to a cart, logging in, searching the contents of a site to find course material, etc...

我同意这一点。行为驱动测试擅长快速“规范”并确保应用程序 行为 正确,并按照预期进行。你可以采用这个定义并说你正在确保你的排序有效,但关键 (IMO) 是你不是在测试你的程序如何工作,而是在测试算法如何工作,这是程序的一个非常具体的元素。如果您查看构建测试的方式,您会发现您正在尝试模仿单元测试:您抛出一堆输入,然后与您尝试获得的输出进行比较。

BDD 测试旨在测试一个功能,如场景。例如,如果您有一个从文件中组织 phone 数字的程序,排序将是该过程的一部分,但您不单独介绍“排序”,而是测试程序的一般行为(如果您获得了执行应用程序所产生的预期文件)。您设置测试,执行它,验证结果。

您还应该在编写代码之前编写测试,我同意@Theo Lenndoff 的观点,将输入作为 属性 很奇怪而且非常违反直觉。 (你也提到了)。

两种风格选择

单元测试 (TDD) 方法和 BDD 方法之间的选择归结为偏好。如果客户要求 BDD,请提供 BDD。如果团队更习惯 TDD,请使用 TDD。

混合两种方法

选择不排他。我有混合使用这两种方法的经验。要回答在两种方法之间划清界线的问题,Agile Testing Quadrants 非常有帮助:

画线

我发现单元测试 (TDD) 方法对 面向测试的技术 更有帮助。 BDD 方法对 面向业务的测试更有帮助

有关此观察的详细信息

将业务需求映射到 BDD 样式测试更为自然。要测试具有一定商业价值的业务需求,通常需要集成多个 classes。根据我的经验,那些 BDD 风格的测试通常是集成测试,并且具有 功能测试 用户验收测试 .

的特征

另一方面,TDD 测试是由程序员为程序员编写的。许多程序员对单元测试 (TDD) 方法更熟悉或更有经验。这些测试通常 classes 单独测试 并且强调系统的边缘情况和技术方面。

您提供的示例非常例外,因为它在单个 class 中映射了一个业务案例。在这种情况下,两种方法都可以。

首先,我的经验是 BDD 思维在测试单元时有时会有所帮助。

其次,我认为“Given”关键字被滥用了。您能否要求您的客户将他们的场景重写为:

Scenario Outline: Perform a sort
When I sort these integers: '<input>'
Then the result should be '<expected>'

我发现 Given 的正确用法是针对必须存在于被测系统中的对象。在您的示例中,它被用于用户头脑中存在的东西:在代码中实现它必然是不自然的。

第三,如果我必须实施“给定”子句,我会将整数列表存储在测试环境中,而不是尝试立即将它们插入到应用程序中。然后“When”步骤可以从测试环境中检索它们并在代码需要它们的地方使用它们。