【问题标题】:Best practice or workaround for RSpec specs faking class constantsRSpec 规范伪造类常量的最佳实践或解决方法
【发布时间】:2012-11-16 16:15:48
【问题描述】:

假设我有汽车和机械师课程。车有“跑”的方法。机械师出于某种原因需要汽车。然后我编写 RSpec 规范。在 mechanic 中,我定义了一个这样的假类:

class Car; end

然后存根 mechanic 使用的方法。如果我单独运行测试,一切正常。但是当我同时运行这两个测试时(rspec spec/directory/),我的 Mechanic 规格使用真正的 Car 类。

所以。我想这是因为 ruby​​ 类是“开放的”,并且我已经为 Car 规格加载了一次该类。但是有没有更好的方法来做到这一点?这种情况的最佳做法是什么?这是否意味着我的代码需要一些改进,因为它可能是紧密耦合的?

我在 github 上做了一个快速演示:https://github.com/depy/RspecTest

【问题讨论】:

    标签: ruby unit-testing testing rspec


    【解决方案1】:

    Rspec 已内置支持 stubbing constants

    【讨论】:

      【解决方案2】:

      我认为你需要的是两层测试:

      • 单元规格:单独测试每个类
      • 集成规范:整体测试

      给定代码如下:

      class Car
      end
      
      class Mechanic
        def fix(car)
          # do something here
        end
      end
      

      对于单元规格,我会存根依赖关系,例如:

      describe Mechanic do
        let(:mechanic) { described_class.new }
        let(:car)      { stub(stubbed_method_on_car: 14) } # Or just OpenStruct.new(stubbed_method_on_car: 14)
      
        it 'does some stuff' do
          mechanic.fix(car).should eq true
        end
      end
      

      对于集成规范,我会这样做:

      describe Mechanic do
        let(:mechanic) { FactoryGirl.create(:mechanic) }
        let(:car)      { FactoryGirl.create(:car) }
      
        it 'does some stuff' do
          mechanic.fix(car).should eq true
        end
      end
      

      【讨论】:

      • 对于单元规格,您能否描述两个 let 方法的块在做什么?
      • 嗯。实际示例是我有 NotificationService,它具有由 Task 实例调用的发布方法。在 NotificationService.publish 中创建并发布了 Notification 对象。我不想让任务知道关于 NotificationClass 的任何事情,它只知道它可以使用 NotificationService 进行发布,而该服务会完成所有其他事情。但是我如何对其进行单元测试并能够一起运行所有单元测试而不是单独运行。我不希望 NotificationService 使用真正的 Notification 类,因为 Notification 测试对其进行了加载。我希望它说清楚了...... :)
      • @Knownasilya:简而言之 let 设置实例变量 :mechanic 的访问器 -> @mechanic 在每个示例后重置为原始状态
      • @MatjazMuhic:我理解正确吗? Task 在内部使用 NotificationService.publish?如果是,您可以通过NotificationService.stub(:publish) 存根此方法,或者如果您使用的是NotificationService 的实例,它将是:NotificationService.any_instance.stub(:publish)。我认为您必须提供更清晰的示例,以便我可以为您提供帮助:)
      • 让我这样说。来自我的 github 示例。汽车规格需要一个 Car 类,如果我想在 Mechanic 规格中创建一个假货,我不能让它以某种方式使用 Car 规格加载的 Car 类。
      【解决方案3】:

      因为 Ruby 类是开放的,所以这个假类将无法工作。

      您可以使用的一种方法是使用let 以您想要的方式初始化对象,并在需要时使用 before 块上的关系。在 before 块中也欢迎存根。 =p

      希望对你有帮助!

      【讨论】:

      • 但有时当我需要某个类时,该类包含其他内容。我必须在要求之前伪造那个“其他”。那怎么办?
      • 这就是我告诉你在前块做的行为。就像你可以有 2 个 let,每个类有 1 个,在 before 块上,你可以创建一个存根,根据第一个调用返回另一个对象。
      猜你喜欢
      • 2011-12-16
      • 2011-04-08
      • 2021-07-15
      • 2010-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-31
      • 1970-01-01
      相关资源
      最近更新 更多