在哪里使用`FactoryGirl.build_stubbed`以及在哪里使用RSpec的`mock` /`double`

时间:2012-05-31 15:26:17

标签: ruby-on-rails ruby unit-testing rspec factory-bot

我很好奇人们在编写RSpec规范时倾向于使用FactoryGirl.build_stubbed以及他们使用double的位置。也就是说,是否有最佳实践,例如“仅在相应的模型规范中使用FactoryGirl方法?”

当您在spec / models / bar_spec.rb中发现自己使用FactoryGirl.create(:foo)时,它是否有代码味道?

如果您在spec / models / bar_spec.rb中使用FactoryGirl.build_stubbed(:foo),那么代码气味是否较少?

如果你在foos_controller_spec.rb中使用FactoryGirl.create(:foo),这是代码味道吗?

如果你在foos_controller_spec.rb中使用FactoryGirl.build_stubbed(:foo),它是不是代码味道?

如果您在spec / decorators / foo_decorator_spec.rb中使用FactoryGirl.build_stubbed(:foo),这是代码味道吗?

抱歉这么多问题!我很想知道其他人如何在单元测试隔离和面向对象设计最佳实践中划清界限。

谢谢!

1 个答案:

答案 0 :(得分:17)

我认为有一些最佳实践可以指导我们考虑何时使用模拟(在这种情况下为“双重”)与针对真实依赖项进行集成(在本例中为“工厂”)。有一本非常好的测试书(警告:它使用Java示例)描述了测试驱动开发的目的,我认为它在Rails应用程序中的测试讨论中非常有用。它描述了测试的意图如下:

  

...我们在测试驱动开发中的意图是使用模拟对象来显示对象之间的关系。

     

弗里曼,史蒂夫; Pryce,Nat(2009-10-12)。面向对象的软件越来越多,以测试为导向(Kindle Locations 3878-3879)。培生教育(美国)。 Kindle版。

如果我们考虑强调使用测试驱动开发不仅要阻止我们引入回归,而且要帮助我们思考我们的代码如何根据其接口和与其他对象的关系来构建,我们自然会使用模拟在很多情况下。我将在下面描述这是如何适用于您的具体问题的。

首先,关于我们是否在模型测试中使用模拟对象或实际依赖项 - 如果我们测试类Foo及其对Bar的依赖性,我们可能想要将模拟替换为Bar。通过这种方式,我们将清楚地看到与Bar的耦合程度,因为我们必须模拟将在其上调用的方法。如果我们发现我们对Bar的模拟是复杂的,那么可能我们应该重构Foo和Bar,以便它们彼此之间的耦合较少。

从某种意义上说,Factory.createFactory.build_stubbed具有相同的效果,可以防止你对相关类的显式依赖,我认为它们都是臭的,而Factory.create就是两个选项中的较慢。

在我的测试中,我倾向于不太担心在控制器中模拟外部依赖项。我知道这比完全模拟运行速度慢,并且你没有使控制器与模型明确耦合的好处,但是编写测试的速度更快,而且我通常不担心要清除它控制器与它们管理的持久记录之间的关系。只要你遵循“瘦调控制器”的模式,无论如何都不应该过于担心。如果我们需要在这里指定一个“测试气味”的水平,我会说它比依赖于其他工厂的模型测试更不臭。

我更倾向于担心依赖于他们装饰的类的工厂的装饰器。这是因为根据定义,装饰器应该保持与它们装饰的类相同的接口。装饰通常使用某种形式的继承来实现,无论是使用method_missing委托给decoratee,还是通过decoratee的显式子类化。正因为如此,如果装饰器偏离它所装饰的东西的界面太多,你就会破坏像Liskov Substitution这样的良好的面向对象编程的其他规则。只要你的装饰没有通过破坏良好继承的规则来实现,那么你所装饰的类的耦合已经存在,所以如果你对decoratee的测试取决于持久性或存根,那么它就不会让事情变得更糟它装饰的东西的工厂。你可以在装饰测试中对工厂发疯,这与IMO无关。

我认为重要的是要注意,即使您在大多数情况下更喜欢模拟,您仍应该使用一些使用真实依赖项的集成测试。您会发现这些内容涵盖了特定的高价值案例,其中隔离的单元测试可以提供更多类别的功能。

无论如何,我有时会破坏所有上述规则,它们只是我在编写测试时使用的一些指导原则。我期待着在他们的测试中听到其他人如何使用工厂(build_stubbed并且真正坚持)与模拟对象(双打)。