测试时我应该在 Factory girl 中还是在规范文件中存根模型?

Posted

技术标签:

【中文标题】测试时我应该在 Factory girl 中还是在规范文件中存根模型?【英文标题】:Should I stub the model in Factory girl or in the spec file while testing? 【发布时间】:2012-02-22 17:55:32 【问题描述】:

我遇到的几乎每个规范文件最终都会编写如下内容:

  before :each do
    @cimg = Factory.build :cimg_valid
    @cimg.stub(:validate_img).and_return true
    @cimg.stub(:validate_img_url).and_return true
    @cimg.stub(:save_images).and_return true
    @cimg.stub(:process_image).and_return true
    @cimg.stub(:img).and_return true
  end

我的意思是,我从 Factory.build 获得的模型是完全有效的。但是,如果我不存根那些东西,它会将东西保存在文件系统中,并验证我没有测试的东西......

我的意思是,我认为这样做会更干净:

  before :each do
    @cimg = Factory.build :cimg_for_testing_tags
  end

如果在工厂内存根甚至是可能的。

存根模型的正确方法是什么?

【问题讨论】:

【参考方案1】:

@fkreusch 的答案在您使用新的 RSpec expect() 语法 (3.0+) 之前效果很好

将其放入 rails_helper.rb 对我有用:

FactoryBot::SyntaxRunner.class_eval do
  include RSpec::Mocks::ExampleMethods
end

在 OP 的示例中,您现在可以这样做:

FactoryBot.define do
  factory :cimg_for_testing_tags do

    ... # Factory attributes

    after(:build) do |cimg|
      allow(cimg).to receive(:validate_img)  true 
    end
  end
end

来源:github.com/printercu,参见:https://github.com/thoughtbot/factory_bot/issues/703#issuecomment-83960003

【讨论】:

执行此处描述的操作现在会出现以下错误(FactoryGirl v4.7.0):The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported. @jsears 我遇到了这个错误,因为我的rails_helper.rbbefore(:suite) 块中运行FactoryGirl.lint,并且无法在那里设置存根。我通过将 lint 调用包装在 RSpec::Mocks.with_temporary_scope ... 中解决了这个问题。 请注意,FactoryGirl 现在是 FactoryBot,因此在您的示例中这两个名称应该更改。【参考方案2】:

在最新版本的 factory_girl 中,你有一个 after_build 回调,所以我相信你可以这样定义你的工厂:

FactoryGirl.define do
  factory :cimg_for_testing_tags do

    ... # Factory attributes

    after_build do |cimg|
      cimg.stub(:validate_img).and_return true
    end
  end
end

更新

factory_girl 3.3.0 之后,语法改为:

FactoryGirl.define do
  factory :cimg_for_testing_tags do

    ... # Factory attributes

    after(:build) do |cimg|
      cimg.stub(:validate_img).and_return true
    end
  end
end

【讨论】:

但是我应该这样做吗?还是应该在规范文件中存根? 我认为,如果您重复自己很多次,那么在这种情况下使用通用存根是有意义的,只需确保 :cimg_for_testing_tags 有一个父工厂,在您想要的情况下没有存根测试实际行为。 这是一个很棒的技术。我发现自己在为这种情况寻找解决方案而苦苦挣扎。谢谢@fkreusch! 这不再有效。在最新的 rspec 中,这将导致失败 "Using 'stub' from rspec-mocks' old ':should' syntax without explicitly enabling the syntax is deprecated." 不幸的是,如果您切换到使用新的 rspec allow() 语法,您会遇到不同的问题,因为 FactoryGirl 不包含 RSpec 的方法:NoMethodError: undefined method 'allow' for #<FactoryGirl::SyntaxRunner:0x007f95469cc0d8>。有谁知道适用于最新版本的解决方案? 您可以使用def cimg.validate_img; true; end 并在没有 rspec 的情况下有效地将其存根。【参考方案3】:

工厂应该生产“真实世界”的对象,因此在工厂中改变行为(即存根)是一种不好的做法(并且容易出错)。

你可以的

let(:user) instance_double(User, FactoryGirl.attributes_for(:user))

before do
  allow(user).to receive(:something).and_return('something')
end

如果您的 before 子句太大,您可能需要将其提取到单独的方法中,或者创建一个模拟子类来覆盖您要存根的方法。

【讨论】:

【参考方案4】:

您也可以考虑使用FactoryGirl#build_stubbed。

【讨论】:

以上是关于测试时我应该在 Factory girl 中还是在规范文件中存根模型?的主要内容,如果未能解决你的问题,请参考以下文章

如果在描述块中调用 Factory Girl 为啥会创建重复项

跳过 Factory Girl 和 Rspec 的回调

Rails 4 使用 Engine 中的 Factory Girl 工厂

在 factory_girl 中填充与儿童的关联

在具有唯一约束的关联中使用 Rails 中的 factory_girl。得到重复的错误

Factory Girl + Mongoid 在夹具中嵌入文档