使用FactoryGirl加速模型规范中的关联 - 创建vs build vs build_stubbed

GMA*_*GMA 8 unit-testing rspec ruby-on-rails factory-bot

说我有机型UserPost,用户has_many的职位和岗位belongs_to的用户.

当我写一个规范时Post,我的第一直觉是写这样的东西:

before do
  @user = FactoryGirl.create :user
  @post = @user.posts.new(title: "Foo", content: "bar)
end

... tests for @post go here ...
Run Code Online (Sandbox Code Playgroud)

但这将创建一个新的用户 - 命中数据库 - 对于每一次测试,这将减慢速度.有没有更好的方法来加快我的测试速度并避免频繁击中数据库?

据我所知,我不能使用FactoryGirl.build :user因为,即使它不会打到数据库,关联也无法正常工作,因为@user没有ID,所以@post.user不会工作(它返回nil.)

我可以使用FactoryGirl.build_stubbed :user哪个创建了一个"假@user持有" ,它有一个ID,但@post.user仍然返回nil.当我测试与协会有关的事情时,是否build_stubbed有任何实际优势build

我想我可以使用build_stubbed存根,@post.user所以它返回@user...有什么理由这可能是个坏主意吗?

或者我应该使用create并接受速度命中?

我能想到的唯一另一种选择是在一个before(:all)块中设置@user 似乎是一个坏主意.

以简洁明了的方式编写这类测试的最佳方法是什么,避免进行过多的数据库查询?

ush*_*sha 18

如果您不希望您的测试访问数据库,那么您就必须这样做.

before do
  @user = FactoryGirl.build_stubbed :user
  @post = FactoryGirl.build_stubbed :post
  @user.stub(:posts).and_return([@post])
  @post.stub(:user).and_return(@user)
end
Run Code Online (Sandbox Code Playgroud)

注意:使用时要小心before(:all).它不会在事务中执行.因此,无论您创建什么,before(:all)都会留在数据库中,并可能导致与其他测试冲突

关于FactoryGirl.build,它构建对象,但创建关联.

例如:

factory :user do
  association posts
end

FactoryGirl.build(:user) #this creates posts in the database even though you are only building the parent object(user)
Run Code Online (Sandbox Code Playgroud)


Joe*_*ris 15

简答

@user = FactoryGirl.build_stubbed(:user)
@post = FactoryGirl.build_stubbed(:post, :user => @user)
Run Code Online (Sandbox Code Playgroud)

这将使@ post.user工作,而不会访问数据库.

答案很长

我的建议是等待before你确定你需要它.相反,为每个单独的测试构建所需的数据,并在找到时将重复提取到方法或新工厂.

另外,您是否真的需要在每次测试中引用用户?有@user在每个测试可说给其他开发者,它是重要的无处不在.

最后,假设用户关联在您的文章的工厂也宣告,你会自动获得一个工作post.user,当你做build_stubbed(:post).


Den*_*nis 9

它可以很容易忘记之间的差异create,build以及build_stubbed.以下是相同情况下的快速参考(因为此页面在搜索结果中排名很高).

# Returns a User instance that's not saved (does not write to DB)
user = build(:user)

# Returns a saved User instance (writes to DB)
user = create(:user)

# Returns a hash of attributes that can be used to build a User instance
attrs = attributes_for(:user)

# Returns an object with all defined attributes stubbed out
stub = build_stubbed(:user)

# Passing a block to any of the methods above will yield the return object
create(:user) do |user|
  user.posts.create(attributes_for(:post))
end
Run Code Online (Sandbox Code Playgroud)

资源