RSpec: Sự khác biệt giữa let và a before block là gì?


90

Sự khác biệt giữa letvà một beforekhối trong RSpec là gì?

Và khi nào thì sử dụng từng cái?

Cách tiếp cận tốt (cho hoặc trước) trong ví dụ dưới đây sẽ là gì?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

Tôi đã nghiên cứu bài đăng stackoverflow này

Nhưng liệu nó có tốt để xác định những thứ như trên?


1
Về cơ bản, 'let' được sử dụng bởi những người không thích biến cá thể. Một lưu ý nhỏ là bạn nên cân nhắc sử dụng FactoryGirl hoặc một công cụ tương tự.
apneadiving

Câu trả lời:


146

Mọi người dường như đã giải thích một số cách cơ bản mà chúng khác nhau, nhưng lại bỏ qua before(:all)và không giải thích chính xác tại sao chúng nên được sử dụng.

Tôi tin rằng các biến cá thể không có chỗ được sử dụng trong phần lớn các thông số kỹ thuật, một phần do những lý do được đề cập trong câu trả lời này , vì vậy tôi sẽ không đề cập đến chúng như một tùy chọn ở đây.

để khối

Mã trong một letkhối chỉ được thực thi khi được tham chiếu, việc tải chậm điều này có nghĩa là thứ tự của các khối này không liên quan. Điều này cung cấp cho bạn một lượng lớn năng lượng để cắt giảm thiết lập lặp lại thông qua các thông số kỹ thuật của bạn.

Một ví dụ (cực kỳ nhỏ và nguyên bản) về điều này là:

let(:person)     { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }

context 'with a moustache' do
  let(:has_moustache) { true } 
  its(:awesome?)      { should be_true }
end

context 'without a moustache' do
  let(:has_moustache) { false } 
  its(:awesome?)      { should be_false }
end

Bạn có thể thấy điều đó has_moustacheđược định nghĩa khác nhau trong mỗi trường hợp, nhưng không cần phải lặp lại subjectđịnh nghĩa. Một điều quan trọng cần lưu ý là letkhối cuối cùng được xác định trong ngữ cảnh hiện tại sẽ được sử dụng. Điều này rất tốt cho việc thiết lập một mặc định được sử dụng cho phần lớn các thông số kỹ thuật, có thể được ghi đè nếu cần.

Ví dụ: kiểm tra giá trị trả về calculate_awesomenếu được truyền vào một personmô hình với top_hatđặt thành true, nhưng không có ria mép sẽ là:

context 'without a moustache but with a top hat' do
  let(:has_moustache) { false } 
  let(:person)        { build(:person, top_hat: true) }
  its(:awesome?)      { should be_true }
end

Một điều cần lưu ý khác về khối lệnh let, chúng không nên được sử dụng nếu bạn đang tìm kiếm thứ gì đó đã được lưu vào cơ sở dữ liệu (tức là Library.find_awesome_people(search_criteria)) vì chúng sẽ không được lưu vào cơ sở dữ liệu trừ khi chúng đã được tham chiếu. let!hoặc beforecác khối là những gì nên được sử dụng ở đây.

Ngoài ra, đừng bao giờ sử dụng befoređể kích hoạt thực thi các letkhối, đây là những gì let!được tạo ra!

để cho! khối

let!các khối được thực thi theo thứ tự chúng được xác định (giống như khối trước). Một điểm khác biệt cốt lõi đối với các khối trước là bạn nhận được một tham chiếu rõ ràng đến biến này, thay vì cần phải quay lại các biến phiên bản.

Như với letcác khối, nếu nhiều let!khối được xác định có cùng tên, thì gần đây nhất là những gì sẽ được sử dụng trong quá trình thực thi. Sự khác biệt cốt lõi là let!các khối sẽ được thực thi nhiều lần nếu được sử dụng như vậy, trong khi letkhối sẽ chỉ thực thi lần cuối cùng.

trước (: mỗi) khối

before(:each)là mặc định trước khối, và do đó có thể được tham chiếu before {}thay vì chỉ định đầy đủ before(:each) {}mỗi lần.

Sở thích cá nhân của tôi là sử dụng beforecác khối trong một vài tình huống cốt lõi. Tôi sẽ sử dụng trước các khối nếu:

  • Tôi đang sử dụng chế độ chế giễu, khai gian hoặc tăng gấp đôi
  • Có bất kỳ thiết lập có kích thước hợp lý nào (nói chung đây là dấu hiệu cho thấy các đặc điểm ban đầu của bạn chưa được thiết lập chính xác)
  • Có một số biến mà tôi không cần tham chiếu trực tiếp nhưng được yêu cầu để thiết lập
  • Tôi đang viết các bài kiểm tra bộ điều khiển chức năng trong rails và tôi muốn thực hiện một yêu cầu cụ thể để kiểm tra (tức là before { get :index }). Mặc dù bạn có thể sử dụng subjectcho điều này trong nhiều trường hợp, nhưng đôi khi nó có vẻ rõ ràng hơn nếu bạn không yêu cầu tham chiếu.

Nếu bạn thấy mình đang viết các beforekhối lớn cho thông số kỹ thuật của mình, hãy kiểm tra các nhà máy của bạn và đảm bảo rằng bạn hiểu đầy đủ các đặc điểm cũng như tính linh hoạt của chúng.

trước (: tất cả) khối

Chúng chỉ được thực thi một lần, trước các thông số kỹ thuật trong ngữ cảnh hiện tại (và con của nó). Những điều này có thể được sử dụng để mang lại lợi ích lớn nếu được viết chính xác, vì có một số tình huống nhất định, điều này có thể cắt giảm việc thực hiện và nỗ lực.

Một ví dụ (hầu như không ảnh hưởng đến thời gian thực thi) là chế nhạo một biến ENV để kiểm tra, mà bạn chỉ cần thực hiện một lần.

Hy vọng rằng sẽ giúp :)


Đối với người dùng nhỏ nhất, tôi là người nhưng tôi không nhận thấy thẻ RSpec của phần Hỏi và Đáp này, before(:all)tùy chọn không tồn tại trong Minitest. Dưới đây là một số cách giải quyết trong phần nhận xét: github.com/seattlerb/minitest/issues/61
MrYoshiji

Bài viết được tham chiếu là một liên kết chết: \
Dylan Pierce

Cảm ơn @DylanPierce. Tôi không thể tìm thấy bất kỳ bản sao nào của bài báo đó, vì vậy tôi đã tham khảo một câu trả lời SO để thay thế cho vấn đề này :)
Jay

Cảm ơn :) nhiều đánh giá cao
Dylan Pierce

1
itskhông còn trong rspec-core. Một cách thành ngữ hiện đại hơn là it { is_expected.to be_awesome }.
Zubin

26

Hầu như luôn luôn, tôi thích hơn let. Bài đăng mà bạn chỉ định liên kết letcũng nhanh hơn. Tuy nhiên, đôi khi, khi nhiều lệnh phải được thực hiện, tôi có thể sử dụng before(:each)vì cú pháp của nó rõ ràng hơn khi nhiều lệnh liên quan.

Trong ví dụ của bạn, tôi chắc chắn muốn sử dụng letthay vì before(:each). Nói chung, khi chỉ một số khởi tạo biến được thực hiện, tôi có xu hướng thích sử dụng let.


15

Một sự khác biệt lớn chưa được đề cập là các biến được định nghĩa với letkhông được khởi tạo cho đến khi bạn gọi nó lần đầu tiên. Vì vậy, trong khi một before(:each)khối sẽ khởi tạo tất cả các biến, letchúng ta hãy xác định một số biến mà bạn có thể sử dụng trong nhiều thử nghiệm, nó không tự động khởi tạo chúng. Nếu không biết điều này, các bài kiểm tra của bạn có thể quay trở lại gây khó khăn cho chính bạn nếu bạn đang mong đợi tất cả dữ liệu được tải trước. Trong một số trường hợp, bạn thậm chí có thể muốn xác định một số letbiến, sau đó sử dụng một before(:each)khối để gọi từng letphiên bản chỉ để đảm bảo dữ liệu có sẵn để bắt đầu.


20
Bạn có thể sử dụng let!để xác định các phương thức được gọi trước mỗi ví dụ. Xem tài liệu RSpec .
Jim Stewart

3

Có vẻ như bạn đang sử dụng Machinist. Hãy cẩn thận, bạn có thể gặp một số vấn đề với make! bên trong let (phiên bản không nổ) xảy ra bên ngoài giao dịch cố định toàn cầu (nếu bạn cũng đang sử dụng thiết bị giao dịch), do đó làm hỏng dữ liệu cho các thử nghiệm khác của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.