为什么这个RSpec示例需要“let!”而不是“让”?

时间:2016-04-18 12:14:43

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

我能够使用以下代码进行测试,但它似乎很奇怪,我并不完全理解它。

有人可以告诉我,以这种方式创建对象是最佳的吗?

为什么我只需要let!用于第二次post_comment_reply创建,为什么我不能用于其余的对象?

post_comment.rb

belongs_to :post, touch: true
belongs_to :user
has_many :post_comment_replies, dependent: :destroy
has_many :users, through: :post_comment_replies

def send_post_comment_reply_creation_notification(reply)
  post_repliers = ([user] + [post.user] + users).uniq - [ reply.user ]
  post_repliers.each do |replier|
    Notification.create(recipient_id: replier.id, sender_id: reply.user_id, notifiable: self.post, action: "commented")
  end
end

post_comment_spec.rb

describe "instance methods" do
  let(:post_user) { create(:user) }
  let(:comment_user) { create(:user) }
  let(:reply_user) { create(:user) }
  let(:reply_user_2) { create(:user) }
  let(:post_reader) { create(:user) }
  let(:post) { create(:post, user: post_user) }
  let(:post_comment) { create(:post_comment, user: comment_user) }
  let(:post_comment_reply) { create(:post_comment_reply, post_comment: post_comment, user: reply_user) }
  let!(:post_comment_reply_2) { create(:post_comment_reply, post_comment: post_comment, user: reply_user_2) }


  it "send_post_comment_reply_creation_notification" do
    expect{
      post_comment.send_post_comment_reply_creation_notification(post_comment_reply)
    }.to change{Notification.count}.by(3)
  end

end

2 个答案:

答案 0 :(得分:3)

let很懒。如果您没有引用它,它就不会被评估,在您的情况下,副作用不会发生(副作用是创建数据库条目)。

另一方面,

let!始终被评估。

答案 1 :(得分:1)

为什么你需要一个let! let是懒惰的(它仅在被引用时运行); let!非常渴望(它在测试之前运行,无论是否提及)。您的测试需要创建:post_comment_reply两次; let一个有效,因为测试引用它,但let!一个没有被引用,所以它必须是let!,而不是let

它是最佳的吗?您的测试设置有效,但正如我们发现的那样,它并不是那么清晰。它还为任何向包含describe的{​​{1}}块添加更多测试的人设置陷阱:在每次测试之前将创建该对象是否需要,减慢所有测试和可能影响结果。

相反,我删除了let!并写了这个(let!未显示):

let

通常,更喜欢在示例(describe '#send_post_comment_reply_creation_notification' do it "notifies each user who replies to the post_comment" do create(:post_comment_reply, post_comment: post_comment, user: reply_user_2) expect { post_comment.send_post_comment_reply_creation_notification(post_comment_reply) }. to change { Notification.count }.by(3) end end 块)中而不是在it块中创建工厂对象。实际上,除了您在多个示例中实际使用let!变量之外,还希望在示例中创建let。 (你只展示了一个例子,但我怀疑在同一个let块中确实存在更多。)如果你只在一次测试中使用工厂对象,那么没有理由让读者在你周围寻找测试文件的定义位置,或定义其他测试中可用的名称,无论是否在此处使用。