我为什么要写一个假类和单元测试呢?

时间:2008-11-24 14:42:14

标签: unit-testing mocking

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

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

我为什么写这个?我正在编写的任何内容在部署时都会被软件实际使用,对吧?我真的没有看到这个练习的价值。

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

混淆...

有人能指出我正确的方向来帮助我理解这个吗?

谢谢!

10 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分:3)

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

测试支持类的重点是要有一些可以预测其行为的东西。如果您需要测试测试支持类以预测其行为,则存在问题。

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

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

  • 模拟 - 由模拟框架创建
  • 伪造 - 手动创建,可能实际上有些功能。
  • 测试支持 - 模拟,伪造,存根以及其他所有内容。不生产。

答案 2 :(得分:2)

模拟/存根对象的目的不是要测试而是你试图测试的单元,它是允许你测试那个单元而不需要其他 classes。

基本上这样你就可以一次测试一个类,而不必测试他们所依赖的所有类。

答案 3 :(得分:1)

谁看着观察者?

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

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

答案 4 :(得分:1)

你不应该测试模拟类。

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

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

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

你做了什么:

  • 你为每个部分写了一个模拟课 (轮子,马鞍等)简单 实现重量位
  • 然后你将那些模拟类传递给Bicycle
  • 测试Bicycle类
  • 上的 GetWeight 方法

通过这种方式,您可以专注于测试Bicycle类上的 GetWeight ,其方式与其他类无关(比如它们尚未实现,不具有确定性等)。

答案 5 :(得分:1)

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

答案 6 :(得分:1)

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

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

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

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

当然,YMMV。 ; - )

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

请查看以下文章以获得对此的详细解释:

http://msdn.microsoft.com/en-us/magazine/cc163358.aspx

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

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

答案 9 :(得分:0)

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

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