为什么这个 RSpec 示例需要 "let!" 而不是 "let"?
Why does this RSpec example require "let!" instead of "let"?
我能够使用以下代码进行测试,但它似乎很奇怪,我并不完全理解它。
谁能告诉我以这种方式创建对象是否是最佳方式?
为什么我必须只使用 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
let
懒惰。如果您不引用它,则不会对其进行评估,并且在您的情况下,不会发生副作用(副作用是创建数据库条目)。
另一方面,let!
总是被评估。
为什么需要 let!
: let
是惰性的(它只在引用时运行); let!
很急切(它在测试之前运行,无论是否被引用)。您的测试需要创建 :post_comment_reply
两次; let
之所以有效,是因为测试引用了它,但是 let!
没有被引用,因此它必须是 let!
,而不是 let
.
它是最优的吗? 你的测试设置有效,但正如我们发现的那样,它并不像它应该的那样清晰。它还为任何向包含 let!
的 describe
块添加更多测试的人设置了一个陷阱:无论是否需要,都会在每次测试之前创建该对象,从而减慢所有测试并可能影响结果.
相反,我会删除 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
变量。 (你只展示了一个例子,但我怀疑在同一个 describe
块中确实有更多。)如果你只在一次测试中使用工厂对象,则没有理由进行 reader 搜索在你的测试文件周围定义它,或者定义一个在其他测试中可用的名称,无论它是否在那里使用。
我能够使用以下代码进行测试,但它似乎很奇怪,我并不完全理解它。
谁能告诉我以这种方式创建对象是否是最佳方式?
为什么我必须只使用 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
let
懒惰。如果您不引用它,则不会对其进行评估,并且在您的情况下,不会发生副作用(副作用是创建数据库条目)。
let!
总是被评估。
为什么需要 let!
: let
是惰性的(它只在引用时运行); let!
很急切(它在测试之前运行,无论是否被引用)。您的测试需要创建 :post_comment_reply
两次; let
之所以有效,是因为测试引用了它,但是 let!
没有被引用,因此它必须是 let!
,而不是 let
.
它是最优的吗? 你的测试设置有效,但正如我们发现的那样,它并不像它应该的那样清晰。它还为任何向包含 let!
的 describe
块添加更多测试的人设置了一个陷阱:无论是否需要,都会在每次测试之前创建该对象,从而减慢所有测试并可能影响结果.
相反,我会删除 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
变量。 (你只展示了一个例子,但我怀疑在同一个 describe
块中确实有更多。)如果你只在一次测试中使用工厂对象,则没有理由进行 reader 搜索在你的测试文件周围定义它,或者定义一个在其他测试中可用的名称,无论它是否在那里使用。