单元测试 - 基于算法或样本?

Oha*_*der 8 c# language-agnostic unit-testing

假设我正在尝试测试一个简单的Set类

public IntSet : IEnumerable<int>
{
    Add(int i) {...}
    //IEnumerable implementation...
}
Run Code Online (Sandbox Code Playgroud)

并且假设我正在尝试测试集合中不​​存在重复值.我的第一个选择是将一些样本数据插入到集合中,并使用我对所用数据的了解来测试重复项,例如:

    //OPTION 1
    void InsertDuplicateValues_OnlyOneInstancePerValueShouldBeInTheSet()
    {
        var set = new IntSet();

        //3 will be added 3 times
        var values = new List<int> {1, 2, 3, 3, 3, 4, 5};
        foreach (int i in values)
            set.Add(i);

        //I know 3 is the only candidate to appear multiple times
        int counter = 0;
        foreach (int i in set)
            if (i == 3) counter++;

        Assert.AreEqual(1, counter);
    }
Run Code Online (Sandbox Code Playgroud)

我的第二个选择是通常测试我的状况:

    //OPTION 2
    void InsertDuplicateValues_OnlyOneInstancePerValueShouldBeInTheSet()
    {
        var set = new IntSet();

        //The following could even be a list of random numbers with a duplicate
        var values = new List<int> { 1, 2, 3, 3, 3, 4, 5};
        foreach (int i in values)
            set.Add(i);

        //I am not using my prior knowledge of the sample data 
        //the following line would work for any data
        CollectionAssert.AreEquivalent(new HashSet<int>(values), set);
    } 
Run Code Online (Sandbox Code Playgroud)

当然,在这个例子中,我方便地有一个要检查的set实现,以及比较集合的代码(CollectionAssert).但如果我没有呢?这段代码肯定比以前的选项更复杂!当您测试现实生活中的自定义业务逻辑时,就会出现这种情况.

当然,对预期条件的测试通常会涵盖更多情况 - 但它变得非常类似于再次实现逻辑(这既乏味又无用 - 您不能使用相同的代码来检查自己!).基本上我问的是我的测试是否应该看起来像"插入1,2,3然后检查一下3"或"插入1,2,3并检查一般情况"

编辑 - 为了帮助我理解,如果您更喜欢选项1或选项2(或两者都没有,或者它取决于案例等),请在答案中说明.只是为了澄清,很明显,在这种情况下(IntSet),选项2在所有方面都更好.但是,我的问题与您没有替代实现进行检查的情况有关,因此选项2中的代码肯定比选项1复杂.

Ali*_*tad 1

基本上我问的是我的测试是否应该类似于“插入 1, 2, 3 然后检查有关 3 的内容”或“插入 1, 2, 3 并检查一般内容”

我不是 TDD 纯粹主义者,但似乎人们说,如果您尝试测试的条件被破坏,测试就应该被破坏。ei 如果您实施检查一般条件的测试,那么您的测试将在多种情况下中断,因此它不是最佳的。

如果我正在测试无法添加重复项,那么我只会测试它。所以在这种情况下,我会说我会先选择。

(更新)

好的,现在您已经更新了代码,我需要更新我的答案。

我会选择哪一个?这取决于 的实施CollectionAssert.AreEquivalent(new HashSet<int>(values), set);。例如,IEnumerable<T>确实保持顺序,HashSet<T>而不保持顺序,因此即使这可能会破坏测试,但不应该破坏测试。对我来说,第一仍然是优越的。