单元测试Add/Get方法对

本文关键字:方法 Get Add 单元测试 | 更新日期: 2023-09-27 18:26:37

我是一名用C#编写代码的.Net开发人员,我被分配到一个项目,该项目的经理希望对其进行彻底的单元测试,主要强调隔离,这样一个逻辑错误理想情况下无法通过一次测试,并且测试之间没有依赖关系。

今天我们讨论测试模式,出现了以下问题:

假设我们有一个名为MyHashTable的对象,它实现了:

void Add(string key, string value);
string GetValue(string key);

我们希望分别测试中的每一种方法。当然,主要的问题是,从逻辑上讲,我们无法获得从未添加过的内容,也无法在没有获得的情况下验证添加了什么。我们听说并阅读过关于存根/嘲笑和其他可能有助于克服这些问题的技术,但无法决定哪种解决方案最具可读性和可维护性。

因此,我征求如何单独测试这些方法的建议/想法,如果可以的话,包括你的建议的利弊。非常感谢。

单元测试Add/Get方法对

如果您想实现完全独立和隔离,那么您很可能必须公开MyHashTable的一些内部。例如,Add向其添加元素的底层集合:

public class MyHashTable<TKey, TValue>
{
    internal Dictionary<TKey, TValue> Storage { get; set; }
    // ...
}

正如你所看到的,这不是很漂亮,但正如我所说,在不暴露的情况下,不可能完全隔离测试这两种方法(正如你所注意到的,这是一种方法)。当然,您可以提供其他初始化类的方法,例如使用构造函数获取集合,但它只会将问题进一步委托给其他人——您需要验证构造函数是否正常工作,并且可能需要Get方法。。。这将使您回到原来的问题。

编辑:

请注意,我并不是建议将揭示内部/实现细节作为唯一的途径。您可以简单地将这些方法一起测试(不是像在单个测试中那样,而是一个使用另一个的测试),这可能是更好的解决方案。当然——如果你的Add在某个时候失败了,Get测试也会失败。但话说回来,你想修复损坏的Add——一旦修复完成,一切都会恢复正常。问题自然在于如何区分是Add还是Get坏了——但这正是我在评论中链接的正确断言失败消息或保护断言概念派上用场的地方。

事实是,有时完全分离和隔离太难实现,或者只是需要你引入糟糕的设计。这也是他们不应该成为最终目标的原因之一。

为什么?单元测试的目的是确保被测试的方法按预期工作。没有任何东西表明你不能使用其他方法来确保测试的方法正常工作

以下测试对我来说很好(对于哈希表实现)

[Fact]
public void AddNewItem()
{
    var hashTable = new MyHashTable();
    hashTable.Add("hello", "world");
    Assert.True(hashTable.Exists("hello"));
    Assert.Equal("world", hashTable["hello"]);
}
[Fact]
public void AddExistingItem()
{
   var hashTable = new MyHashTable();
   hashTable.Add("hello", "world");
   hashTable.Add("hello", "realItem");
   Assert.Equal("realItem", hashTable["hello"]);
}

当然,您可以将内部存储分解为一个接口,并将其注入构造函数:

public class MyHashTable
(
    public MyHashTable(IKeyValueStorage storage)
    {}
    // creates a default storage class
    public MyHashTable()
    {}
)

这允许您模拟存储,从而一次测试一个方法。但这只会使问题进一步恶化。您将如何测试存储实现?

我想说的是HashTable是一个小的孤立单元。用它的方法来测试它是好的。域模型不会有这个问题,因为您可以模拟它们的依赖关系。