【问题标题】:How to design for mockability when there is a dependency?存在依赖关系时如何设计可模拟性?
【发布时间】:2013-03-28 16:49:29
【问题描述】:

我有一个方法,它调用 .NET XDocument.Load() 方法从 url 加载 xml 数据。我想让我的班级单元可测试。

那么如何使该调用可模拟/可单元测试?

private void ProcessData(string url)
{
            // todo: make this mockable
            var xDocument = XDocument.Load(url);
            // the rest of the code
}

我使用的一个解决方案是将 xmlUrlLoader 注入到如下类中:

private readonly Func<string, XDocument> _xmlUrlLoader;

public MyConstructor(Func<string,XDocument> xmlUrlLoader)
{
    _xmlUrlLoader = xmlUrlLoader;
}
private void ProcessData(string url)
{
            // todo: make this mockable
            var xDocument = this._xmlUrlLoader(url);
            // the rest of the code
}

有没有更好的办法?

【问题讨论】:

  • 我会将加载 url 的责任上移一级。因此,有一个函数将数据作为字符串或XDocument 进行处理,然后对其进行薄包装,下载数据并将其传递给处理函数。无需嘲笑。
  • 如果我在哪里,我只需将 xDocument 传递给此方法。
  • @KirillBestemyanov 我无法将 xDocument 传递给它;我不想调用 xDocument.Load,它应该被模拟/隔离。
  • XDocument.Load 不接受网址,它只接受文件名。那么你的代码到底是如何工作的呢?
  • @CodesInChaos 不,它接受 Uri(本地或外部); msdn.microsoft.com/en-gb/library/bb343181.aspx 这是通过网络加载 Xml 文件的最简单方法 :)

标签: c# asp.net unit-testing mocking tdd


【解决方案1】:

我同意注入加载程序是正确的方法我不确定为什么它会是 Func 类型

我希望有一个类似于

的界面
public interface IXmlDocumentLoader
{
    XDocument LoadDocument(string url);
}

然后你的代码看起来像

private readonly IXmlDocumentLoader _xmlUrlLoader;

public MyConstructor(IXmlDocumentLoader xmlUrlLoader)
{
    _xmlUrlLoader = xmlUrlLoader;
}

private void ProcessData(string url)
{
            var xDocument = this._xmlUrlLoader(url);
            // the rest of the code
}

但我认为出于关注点分离的原因,文档加载器绝对应该在它自己的类中。

【讨论】:

  • 这看起来是一种有效的简单注入依赖的方法。缺点是我必须创建一个包装器,该包装器将成为生产代码的一部分,但不能进行单元测试。
【解决方案2】:

我更喜欢将逻辑与访问资源分开。在您的情况下,我会将文档传递给处理功能:

void ProcessData(XDocument xDocument);

您可以在不模拟任何内容的情况下测试该功能。然后,如果您愿意,可以在顶部添加一个薄包装器来进行加载。

void ProcessUrl(string url);
{
    var xDocument = XDocument.Load(url);
    ProcessData(xDocument);
}

您可以使用模拟来对包装器进行单元测试,但我个人认为这并没有多大收获。我更喜欢仅在集成测试中使用这些包装器。

【讨论】:

  • 我理解你关于将逻辑与访问资源分开的观点,这是提高可读性的一个有效点(可以说),但上述内容并不能解决我提出的问题,你会如何模拟它?创建接口和包装器?
  • @TheLight 我主要测试采用XDocument 的版本而不模拟任何内容。我不能说如何或是否会在没有上下文的情况下测试包装器。可能带有IFileSystem 接口,并且可能仅带有集成测试。 |你嘲笑什么以及你如何测试是一个风格问题,我更喜欢尽可能少地嘲笑。其他人有不同的偏好。
  • 我指的是运行 XDocument.Load() 的包装器。我认为 ProcessUrl() 方法是一个测试单元,尽管它调用了另一个方法,所以这就是我需要模拟的原因。测试单元不必总是一个属性或一个方法。可以测试方法甚至类的集合,它们仍然是单元测试。如果我要测试我的代码与其他外部资源/系统(文件、数据库、其他 Web 服务)的集成,那么我会将它们称为集成测试。否则,都是单元测试。
猜你喜欢
  • 2020-01-11
  • 2016-06-14
  • 2019-12-08
  • 1970-01-01
  • 2019-02-27
  • 1970-01-01
  • 2015-12-28
  • 1970-01-01
  • 2019-12-14
相关资源
最近更新 更多