2016-04-18 8 views
1

Ich konnte den Test mit dem folgenden Code machen, aber es scheint komisch zu sein und ich verstehe es nicht völlig.Warum benötigt dieses RSpec Beispiel "Let!" statt "lassen"?

Kann mir jemand sagen, ob das Erstellen der Objekte auf diese Weise optimal ist?

Warum muss ich nur let! für die 2. post_comment_reply Erstellung verwenden und warum nicht ich für den Rest der Objekte?

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

Etwas Unerklärliches passiert hier. 'let!' erzeugt 'post_comment_reply_2', bevor der' it' Block ausgeführt wird. Wie beeinflusst es die Erwartung? Wie schlägt der Test fehl, wenn Sie 'post_comment_reply_2' löschen? –

+0

Zählen ändert sich um 2 anstelle der erwarteten 3. –

+0

Es macht keinen Sinn, dass die Erstellung von 'post_comment_reply_2' * vor * dem Test das Ergebnis beeinflussen würde. 'change' wird' Notification.count' am Anfang des 'it' Blocks betrachten, nachdem' post_comment_reply_2' erstellt wurde. Wie trägt 'post_comment_reply_2' zur Veränderung bei? –

Antwort

3

let ist faul. Wenn Sie es nicht referenzieren, wird es nicht ausgewertet und in Ihrem Fall treten keine Nebenwirkungen auf (Nebeneffekt ist die Erstellung eines Datenbankeintrags).

let!, auf der anderen Seite wird immer ausgewertet.

+0

Sergio, könnten Sie das mit diesem Code erklären? Ich meine, warum die zweite Antwort vorher ausgewertet werden muss und warum der Rest nicht? –

+0

@SzilardMagyar: Sie verwenden 'post_comment_reply' in Ihrem Test. Es wird vor der Logik ausgewertet, die Sie testen. Und es führt zur Bewertung der meisten anderen Vermietungen, weil sie seine Abhängigkeiten sind. Wenn du 'post_comment_reply_2' auch benutzt hättest, hättest du nicht" lassen "müssen, es würde auch funktionieren. –

+0

Ahh, also was in diesem Teil 'expect {post_comment.send_post_comment_reply_creation_notification (post_comment_reply)}' aufgerufen wird, kann mit 'let' verwendet werden, aber der Rest, der benötigt wird, um das richtige Ergebnis mit' change {Notification.count} .by zu bekommen (3) 'muss mit' let! 'Bewertet werden? –

1

Warum brauchen Sie ein let!:let faul ist (es läuft nur, wenn bezeichnet); let! ist eifrig (es läuft vor dem Test, ob bezogen oder nicht). Ihr Test muss zweimal erstellt werden :post_comment_reply; Die let funktioniert, weil der Test darauf bezieht, aber die let! wird nicht bezeichnet, so dass es eine let!, nicht eine let sein muss.

Ist es optimal? Ihr Test-Setup funktioniert, aber wie wir festgestellt haben, ist es nicht so klar wie es sein könnte. Es setzt auch eine Falle für jeden, der dem describe Block let! mehr Tests hinzufügt: Dieses Objekt wird vor jedem Test erstellt, ob es benötigt wird oder nicht, was alle Tests verlangsamt und möglicherweise die Ergebnisse beeinflusst.

Stattdessen würde ich die let! löschen und schreiben diese (let s nicht dargestellt):

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 

Im Allgemeinen bevorzugen die Schaffung Fabrikobjekte in den Beispielen (it Blöcke) und nicht in let! Blöcke. In der Tat bevorzugen Sie die Erstellung in den Beispielen auch let, es sei denn, Sie verwenden tatsächlich die let Variable in mehr als einem Beispiel. (Sie haben nur ein Beispiel gezeigt, aber ich vermute, dass es im selben describe Block wirklich mehr gibt.) Wenn Sie nur ein Factory-Objekt in einem Test verwenden, gibt es keinen Grund dafür, dass der Leser nach der Testdatei sucht oder einen in anderen Tests verfügbaren Namen zu definieren, unabhängig davon, ob er dort verwendet wird oder nicht.

+0

Danke Dave! Es macht jetzt Sinn! –

Verwandte Themen