11

我了解需要测试具有逻辑的类(例如,可以计算折扣的类),您可以在其中测试实际类。

但我刚刚开始为一个将充当存储库(从数据库获取对象)的项目编写单元测试。我发现自己正在编写一个实现ISomethingRepository接口的“假”存储库。它在内部使用一个Dictionary<Guid, Something>用于存储。它实现了接口的Add(Something)GetById(Guid)方法。

我为什么要写这个?我写的东西在部署时不会被软件实际使用,对吧?我真的看不出这个练习的价值。

我还得到了使用模拟对象的建议,我可以提前设置它来满足某些期望。这对我来说似乎更没有意义:当然测试会成功,我已经模拟/伪造它成功了!而且我仍然不确定实际软件在连接到数据库时是否会正常运行......

使困惑...

有人可以指出正确的方向来帮助我理解这一点吗?

谢谢!

4

10 回答 10

14

您不是在测试您的模拟对象,而是在与它交互的其他一些类。因此,您可以例如测试控制器是否将保存方法调用转发到您的假存储库。如果你在“测试你的假对象”,那就有问题了

于 2008-11-24T14:48:19.497 回答
4

不要测试模拟类。使用模拟类测试生产类。

测试支持类的全部意义在于拥有可以预测其行为的东西。如果您需要测试测试支持类以预测其行为,则存在问题。

在您在评论中链接的假数据库文章中,作者需要对他的假数据库进行单元测试,因为它是他的产品(至少在文章的上下文中)。

编辑:更新的条款更加一致。

  • Mock - 通过模拟框架创建
  • 假 - 手动创建,实际上可能会起作用。
  • 测试支持 - 模拟、伪造、存根和所有其他。不是生产。
于 2008-11-24T14:55:32.680 回答
2

模拟/存根对象的目的不是要测试,而是要测试您要测试的单元,它是允许您在不需要其他类的情况下测试该单元。

基本上,您可以一次测试一个类,而不必测试它们也依赖的所有类。

于 2008-11-24T14:47:58.170 回答
1

谁在看守望者?

有趣的是,例如,如果模拟实现为极端情况抛出特定异常,因此您知道使用或依赖 IRepositorySomething 的类可以处理现实生活中抛出的异常。您无法使用测试数据库轻松生成其中一些异常。

您不使用单元测试来测试 Mock 对象,而是使用它来测试依赖它的类。

于 2008-11-24T14:48:54.067 回答
1

您不应该测试模拟类。

您通常做的是:为您正在测试的类与之交互的所有类创建模拟类。

假设您正在测试一个名为Bicycle的类,它接收 Wheel、Saddle、HandleBar 等类的构造函数对象。

然后在你想要测试的 Bike 类中测试它的方法GetWeight,它可能会遍历每个部分并调用它们的属性/方法 Weight,然后返回总数。

你做什么:

  • 你为每个部分(车轮、鞍座等)编写一个模拟类,它只是实现了重量位
  • 然后你将那些模拟类传递给自行车
  • 在 Bicycle 类上测试GetWeight方法

这样,您可以专注于测试 Bicycle 类上的GetWeight,以独立于其他类的方式(比如它们尚未实现,不确定等)

于 2008-11-24T15:22:38.257 回答
1

您可以使用工具(如 Rhino 或Typemock)来模拟它,而不是自己编写假类。这比自己编写所有的模拟要容易得多。就像其他人说的那样,没有必要测试假代码,如果你使用该工具,那就没有代码了。

于 2009-06-18T13:18:20.247 回答
1

实际上,我发现我们在存储库实现测试中使用的模拟类有两种用途。

首先是测试使用您提到的“ISomethingRepository”等效实现的服务。但是,我们的存储库实现是由工厂创建的。这意味着我们确实针对“ISomethingRepository”编写测试,而不是直接针对“MockSomethingRepository”。通过针对接口进行测试,我们可以轻松断言我们测试的代码覆盖率覆盖了 100% 的接口。代码审查提供了对新接口成员进行测试的简单验证。即使开发人员针对工厂返回的模拟运行,构建服务器也有不同的配置来测试工厂在夜间构建中返回的具体实现。它在测试覆盖率和本地性能方面提供了两全其美的效果。

第二种用途是我很惊讶没有其他人提到过的用途。我的团队负责中间层。我们的网络开发人员负责网络产品的前端。通过构建模拟存储库实现,不存在在前端工作开始之前等待数据库建模和实现的人为障碍。可以编写基于 mock 构建的视图,以提供最少量的“真实”数据,以满足 Web 开发人员的期望。例如,可以提供数据以包含最小和最大长度的字符串数据,以验证两者都不会破坏它们的实现等。

由于我们使用的工厂与返回哪个“ISomethingRepository”有关,因此我们有本地测试配置、构建测试配置、生产配置等。我们有意确保项目中的任何团队都没有不合理的等待时间,因为另一个团队的实施时间。最大的等待时间仍然由开发团队提供,但是我们能够以比前端开发更快的速度来开发我们的领域对象、存储库和服务。

当然,YMMV。;-)

于 2009-08-30T22:48:15.050 回答
0

您编写称为 Stub 或 Mock 对象的“假”类,因为您想以简单的方式测试实现而不测试真正的具体类。目的是通过仅测试接口(或抽象类)来简化测试。

在您的示例中,您正在测试具有字典的内容。它可能由数据库真实填充,或者背后有很多逻辑。在您的“假”对象中,您可以通过使所有数据保持不变来简化一切。这样,您只测试接口的行为,而不是具体对象的构建方式。

于 2008-11-24T14:43:59.817 回答
0

请查看以下文章以获得对此的一个很好的解释:

https://web.archive.org/web/20110316193229/http://msdn.microsoft.com/en-us/magazine/cc163358.aspx

基本上,如果你写了一个假对象,结果证明它相当复杂,有时值得对假对象进行单元测试,以确保它按预期工作。

由于存储库可能很复杂,因此为其编写单元测试通常是有意义的。

于 2013-08-17T07:56:34.250 回答
0

通常不需要在数据访问层中运行经典的单元测试。也许您可以使用单元测试框架的功能为您的数据访问类编写集成式单元测试,即集成测试(= 将您的数据访问层代码与数据库集成)。

例如,在 Spring 项目中,您可以使用 Spring Testcontext 在单元测试中启动 Spring 上下文,然后连接到真实数据库并测试查询是否返回正确的结果。您可能需要一个用于单元测试的自己的数据库,或者您可以将它们与开发人员数据库连接起来。

于 2015-03-06T16:53:45.607 回答