我能够使用以下代码进行测试,但它似乎很奇怪,我并不完全理解它。
有人可以告诉我,以这种方式创建对象是最佳的吗?
为什么我只需要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
答案 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
块中确实存在更多。)如果你只在一次测试中使用工厂对象,那么没有理由让读者在你周围寻找测试文件的定义位置,或定义其他测试中可用的名称,无论是否在此处使用。