应该所有类都是可测试的

本文关键字:测试 | 更新日期: 2023-09-27 18:33:43

我正在尝试为我创建的新项目编写单元测试,但我遇到了一个问题,我无法弄清楚我打算编写的类实际上是如何测试的。下面我简化了我试图编写的类,让您了解我想要实现的目标。

所以我有一个XML解析器,它将简单地从给定的URL访问XML文件,提取我需要的数据并将其作为对象返回。所以我的代码看起来像这样(验证和填充尚未完成,但你明白了(:

public UserDetails ParseUserDetails(string request, string username, string password)
{
    var response = new XmlDocument();
    response.Load(string.Format(request + "?user={0}&password={1}", username, password));
    // Validation checks
    return new UserDetails { // Populate object with XML nodes };
}

目前我的类不可测试。我无法模拟负载来抛出 WebException 以查看我的类如何处理错误,并且在我通过有效的 URL 之前,当我针对该类运行测试时,它总是会抛出异常。我也无法测试从类返回的数据,因为我无法模拟 XML 文档,因为它是从另一个 URL 加载的。

我可以将其拆分为一个可模拟的对象,该对象从URL中检索XML并将其命名为IXmlDocumentLoader之类的名称,但后来我遇到了同样的问题,我有一个这样的类:

public class XmlDocumentLoader : IXmlDocumentLoader
{
    public XmlDocument LoadXmlDocument(string request, string username, string password)
    {
        var response = new XmlDocument();
        response.Load(string.Format(request + "?user={0}&password={1}", username, password));
        return response;
    }
}

这将使 ParseUserDetails 方法更易于测试,但现在类 XmlDocumentLoader 不可测试。那么我只是把问题移到了别处了吗?我的问题是,所有类都必须是可测试的,还是我误解了单元测试?

应该所有类都是可测试的

这肯定是一个"意见"问题,因此,可能会被关闭。

但我会给你一个建议。

拆分一切。使用"一个对象只能做一件事"的原则。加载文件是一回事,验证它是另一回事。如果将两者分开,则可以同时测试两者。

您可以在通用文件(不必是生产站点(上测试下载系统,并测试系统是否正常工作。您可以提供一个"假文件"来测试验证,这也不必是生产文件。

这两个测试都会让你了解你的代码在多大程度上按预期工作。

MSDN 中的 XmlDocument.Load(字符串文件名( 文档这样定义filename参数:

文件名:包含要加载的 XML 文档的文件的 URL。URL 可以是本地文件,也可以是 HTTP URL(Web 地址(。

(强调我的(

因此,如果您的ParseUserDetails测试传递类似 file://C:/path/to/my/test/file 的内容作为 request 参数,则您的代码是完全可测试的。它们可能看起来像这样:

    public void SomeRandomTest()
    {
        string testFileLocalPath = @"C:'path'to'my'test'file";
        // Code to create an XML file with expected data goes here ...
        UriBuilder ub = new UriBuilder();
        ub.Scheme = "file";
        ub.Host = "";
        ub.Path = testFileLocalPath;
        string request = ub.ToString();
        var target = new SomethingThatReadsXml();
        var details = target.ParseUserDetails(request, "dummy", "whocares");
        // Compare returned user details to expected values here ...
    }

这个问题并不容易,我不同意这个代码应该旨在工作并且不需要测试的观点。就个人而言,我会进行集成测试,以测试您可以在构建期间运行的实际现实生活情况。

为了使 LOGIC 可测试,我会把这个东西分开(就像你提到的(,让你的解析器与 Stream(或等效的(一起工作。基于Streams的IO-Code的好处是,你可以用MemoryStreams伪造数据,所以你可以通过测试中编写的XML代码来测试你的解析器。要测试从 Web 检索 XML 并将其作为 Stream 返回的另一件事,模拟起来有点复杂,但逻辑应该容易得多,您只需使用无效的 URL/凭据/...实际检索实际上是 .Net 实现(您不必测试(。

最后,您将到达一个无法伪造的外部边界,但 xml 解析器不应该是该结束;)

在这里你可以看到一个例子:VS杂志