2012-04-16 74 views
26

Tôi đã đọc những gì RSpec manual nói về sự khác biệt, nhưng một số điều vẫn còn khó hiểu. Mỗi nguồn khác, bao gồm "Sách RSpec" chỉ giải thích về "cho phép" và "The Rails 3 Way" cũng khó hiểu như hướng dẫn sử dụng.Rails - RSpec - Sự khác biệt giữa "let" và "let!"

Tôi hiểu rằng "let" chỉ được đánh giá khi được gọi và giữ cùng một giá trị trong phạm vi. Vì vậy, nó có ý nghĩa rằng trong ví dụ đầu tiên trong bài kiểm tra đầu tiên là "let" được gọi chỉ một lần, và kiểm tra thứ hai trôi qua khi nó thêm vào giá trị của bài kiểm tra đầu tiên (được đánh giá một lần trong bài kiểm tra đầu tiên) và có giá trị là 1).

Sau đó, vì "hãy để!" đánh giá khi được xác định, và một lần nữa khi được gọi, nên thử nghiệm không thất bại là "count.should eq (1)" nên có thay vì được "count.should eq (2)"?

Mọi trợ giúp sẽ được đánh giá cao.

Trả lời

8

Nó không được gọi khi được xác định, mà là trước mỗi ví dụ (và sau đó nó được ghi nhớ và không được gọi lại bởi ví dụ). Bằng cách này, số lượng sẽ có giá trị là 1.

Dù sao, nếu bạn có một ví dụ khác, trước khi móc được gọi một lần nữa - tất cả các xét nghiệm sau đây pass:

$count = 0 
describe "let!" do 
    invocation_order = [] 

    let!(:count) do 
    invocation_order << :let! 
    $count += 1 
    end 

    it "calls the helper method in a before hook" do 
    invocation_order << :example 
    invocation_order.should == [:let!, :example] 
    count.should eq(1) 
    end 

    it "calls the helper method again" do 
    count.should eq(2) 
    end 
end 
+0

Đồng ý, đó là lý do tại sao thông số đầu tiên có ý nghĩa. Chắc chắn, mặc dù, spec thứ hai sẽ được đánh giá trước khi ví dụ, (làm cho nó bằng 1) và sau đó một lần nữa khi gọi "count.should" (làm cho nó bằng 2)? –

+3

Không, nó đã được gọi và ghi nhớ, do đó, số lượng $ không tăng trở lại. Dù sao, nếu bạn có một ví dụ khác, móc trước được gọi lại. Xem câu trả lời đã chỉnh sửa của tôi, tôi đã thêm một số mã để làm rõ. – dhoelzgen

+0

Vì vậy, điều đó có nghĩa rằng nếu bạn sử dụng "let" và một spec (một "nó") không doa "đếm.nên "(hoặc tương tự) sau đó tăng sẽ không diễn ra? Nếu đó là trường hợp, sau đó" cho "không nên được coi là một" trước ", vì trước đây theo mặc định ngụ ý các chức năng của" cho phép! "Hoặc Tôi bị mất một cái gì đó một lần nữa? –

21

Bạn có thể đọc thêm về điều này here, nhưng về cơ bản. (:let) được đánh giá uể oải và sẽ không bao giờ được khởi tạo nếu bạn không gọi nó, trong khi (:let!) được đánh giá mạnh trước mỗi cuộc gọi phương thức.

+0

Cảm ơn Justin. Đó là "hướng dẫn" tôi liên kết với điều đó mà tôi không hiểu. Câu hỏi của tôi tập trung nhiều hơn vào lý do tại sao "spec thứ 2" trong liên kết đó được chuyển với "count.should eq (1)" thay vì "count.should eq (2)" như tôi mong đợi. Nếu nó được đánh giá trước khi gọi phương thức, và một lần nữa khi "count.should" được gọi, không phải là ví dụ thứ 2 bằng 2 thay vì 1? –

+1

halle-flippin-lujah, cuối cùng là một lời giải thích không mang về việc ghi nhớ hay bất cứ điều gì. Đơn giản và cho điểm, như mọi thứ nên được. –

2

Tôi cũng đã nhầm lẫn bởi letlet!, vì vậy tôi lấy mã tài liệu từ here và chơi với mã: https://gist.github.com/3489451

Hy vọng điều đó sẽ hữu ích!

+0

Cảm ơn @JonathanLinErnSheong.Nó xóa một số điều lên –

+0

tốt để biết! :) –

4

Tôi cũng nghĩ rằng điều này là khó hiểu, nhưng tôi nghĩ rằng các ví dụ từ The Rails 3 Way là tốt.
cho phép tương tự với các biến mẫu trong khối trước trong khi cho phép! được memoized ngay

Từ Các Rails 3 Way

describe BlogPost do 
    let(:blog_post) { BlogPost.create :title => 'Hello' } 
    let!(:comment) { blog_post.comments.create :text => 'first post' } 

    describe "#comment" do 
    before do 
    blog_post.comment("finally got a first post") 
    end 

    it "adds the comment" do 
     blog_post.comments.count.should == 2 
    end 
    end 
end 

"Kể từ khi khối bình luận sẽ không bao giờ được thực hiện cho khẳng định đầu tiên nếu bạn sử dụng một định nghĩa let, chỉ có một bình luận sẽ có được được thêm vào trong thông số kỹ thuật này mặc dù việc triển khai có thể đang hoạt động. Bằng cách sử dụng let !, chúng tôi đảm bảo rằng nhận xét ban đầu được tạo và thông số sẽ chuyển ngay bây giờ. "

35

Tôi hiểu sự khác biệt giữa letlet! với một ví dụ rất đơn giản. Hãy để tôi đọc câu doc ​​trước, sau đó đưa tay ra.

About let doc nói: -

... letlười biếng đánh giá lại: nó không được đánh giá cho đến khi lần đầu tiên phương pháp xác định nó được gọi.

tôi hiểu sự khác biệt với các ví dụ dưới đây: -

$count = 0 
describe "let" do 
    let(:count) { $count += 1 } 

    it "returns 1" do 
    expect($count).to eq(1) 
    end 
end 

Cho phép chạy ngay bây giờ: -

[email protected]:~/Ruby> rspec spec/test_spec.rb 
F 

Failures: 

    1) let is not cached across examples 
    Failure/Error: expect($count).to eq(1) 

     expected: 1 
      got: 0 

     (compared using ==) 
    # ./spec/test_spec.rb:8:in `block (2 levels) in <top (required)>' 

Finished in 0.00138 seconds (files took 0.13618 seconds to load) 
1 example, 1 failure 

Failed examples: 

rspec ./spec/test_spec.rb:7 # let is not cached across examples 
[email protected]:~/Ruby> 

Tại sao LỖI? Bởi vì, như doc đã nói, với let, nó không được đánh giá cho đến khi lần đầu tiên phương thức nó định nghĩa được gọi. Trong ví dụ , chúng tôi đã không gọi số count, do đó $count vẫn là 0, không tăng thêm 1.

Bây giờ, hãy đến phần let!. Tài liệu đang nói

.... Bạn có thể sử dụng cho phép! để buộc lời gọi của phương thức trước mỗi ví dụ. Điều đó có nghĩa là ngay cả khi bạn không gọi phương thức trợ giúp bên trong ví dụ , vẫn sẽ được gọi trước khi ví dụ của bạn chạy.

Cho phép kiểm tra này cũng: -

Đây là mã sửa đổi

$count = 0 
describe "let" do 
    let!(:count) { $count += 1 } 

    it "returns 1" do 
    expect($count).to eq(1) 
    end 
end 

Cho phép chạy mã này: -

[email protected]:~/Ruby> rspec spec/test_spec.rb 
. 

Finished in 0.00145 seconds (files took 0.13458 seconds to load) 
1 example, 0 failures 

Xem, bây giờ $count lợi nhuận 1, do đó kiểm tra đã được thông qua. Nó xảy ra khi tôi sử dụng let!, chạy trước khi chạy ví dụ, mặc dù chúng tôi đã không gọi count bên trong ví dụ của chúng tôi.

Đây là cách letlet! khác nhau.

+2

đơn giản –

+2

Cảm ơn bạn rất nhiều! Tôi đã googling này cho như hai giờ và cuối cùng tôi theo đứng! –

+0

Hãy nhớ, không bao giờ có một khối cho phép bên trong của một khối trước khi khối, đây là những gì cho phép! được tạo ra cho. Để biết thêm thông tin, hãy xem trang này: https://kolosek.com/rspec-let-vs-before –

1

Và đây là cách để giữ thông số kỹ thuật của bạn có thể dự đoán được.

Bạn nên sử dụng khá nhiều luôn let. Bạn không nên sử dụng let! trừ khi bạn cố ý muốn lưu trữ giá trị qua các ví dụ. Đây là lý do:

describe '#method' do 
    # this user persists in the db across all sub contexts 
    let!(:user) { create :user } 

    context 'scenario 1' do 
    context 'sub scenario' do 
     # ... 
     # 1000 lines long 
     # ... 
    end 

    context 'sub scenario' do 
     # you need to test user with a certain trait 
     # and you forgot someone else (or yourself) already has a user created 
     # with `let!` all the way on the top 
     let(:user) { create :user, :trait } 

     it 'fails even though you think it should pass' do 
     # this might not be the best example but I found this pattern 
     # pretty common in different code bases 
     # And your spec failed, and you scratch your head until you realize 
     # there are more users in the db than you like 
     # and you are just testing against a wrong user 
     expect(User.first.trait).to eq xxx 
     end 
    end 
    end 
end 
Các vấn đề liên quan