单元测试一个简单的集合类

Oha*_*der 5 language-agnostic unit-testing

考虑以下课程:

public class MyIntSet
{
    private List<int> _list = new List<int>();

    public void Add(int num)
    {
        if (!_list.Contains(num))
            _list.Add(num);
    }

    public bool Contains(int num)
    {
        return _list.Contains(num);
    }
}
Run Code Online (Sandbox Code Playgroud)

遵循"仅测试一件事"原则,假设我想测试"添加"功能.考虑以下这种测试的可能性:

[TestClass]
public class MyIntSetTests
{
    [TestMethod]
    public void Add_AddOneNumber_SetContainsAddedNumber()
    {
        MyIntSet set = new MyIntSet();
        int num = 0;

        set.Add(num);
        Assert.IsTrue(set.Contains(num));
    }
}
Run Code Online (Sandbox Code Playgroud)

我对这个解决方案的问题是它实际上测试了两个方法:Add()和Contains().从理论上讲,两者都可能存在错误,只会出现在一个接一个不被调用的情况下.当然,Contains()现在服务器作为List的Contains()的瘦包装器,它本身不应该进行测试,但如果它将来会变得更复杂呢?也许应该始终保留一个简单的"薄包裹"方法用于测试目的?

另一种方法可能建议模拟或暴露(可能使用InternalsVisibleTo或PrivateObject)私有_list成员并让测试直接检查它,但如果有一天内部列表被其他一些集合替换(可能是C5),那么可能会产生测试可维护性问题).

有一个更好的方法吗?我反对上述实现的任何论据都有缺陷吗?

在此先感谢,JC

sle*_*ske 9

你的测试对我来说似乎完全没问题.您可能误解了单元测试的原则.

单个测试应该(理想情况下)只测试一件事,这是真的,但这并不意味着它应该只测试一种方法; 相反,它应该只测试一种行为(不变量,遵守某个业务规则等).

您的测试测试行为"如果您添加到新集合,它不再是空的",这是一个单一的行为:-).

解决你的其他问题:

  • 从理论上讲,两者都可能存在错误,只会出现在一个接一个不被调用的情况下.
    没错,但这只是意味着你需要更多测试:-).例如,添加两个数字,然后调用Contains,或调用Contains而不添加.

  • 另一种方法可能建议模拟或暴露(可能使用InternalsVisibleTo)私有_list成员并让测试直接检查它,但这可能会产生测试可维护性问题[...]
    非常正确,所以不要这样做.单元测试应始终针对被测设备的公共接口.这就是为什么它被称为单元测试,而不是"在一个单元内乱搞"-test ;-).