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

Aar*_*ter 21 ruby unit-testing rspec ruby-on-rails factory-bot

我只是好奇人们在编写RSpec规范时倾向于使用FactoryGirl.build_stubbed和使用的地方double.也就是说,是否有最佳实践,例如"仅在相应的模型规范中使用FactoryGirl方法?"

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

如果你FactoryGirl.build_stubbed(:foo)在spec/models/bar_spec.rb中使用它是不是代码味道?

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

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

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

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

谢谢!

Jus*_*geb 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和真正持久)与模拟对象(双打).