- vừa được xem lúc

Tìm hiểu Subject Rspec cơ bản

0 0 37

Người đăng: Tuan Anh

Theo Viblo Asia

Mở đầu

Khi bắt đầu viết rspec có một keyword mà khiến mình rất khó hiểu và mơ hồ đó là subject. Trong bài viết này sẽ cung cấp cho các bạn đọc những thông tin cơ bản nhất và không còn bối rối với subject nữa.

Theo mô tả trong comment của subject method thì mục đích mang lại giúp chúng ta sử dụng được với phong cách One-liner syntax, tiêu biểu như is_expected.

Định nghĩa :

Nếu truyền vào subject method một cái tên thì sẽ tạo ra một method với tên đó và trả về subject method. Điều này giúp chúng ta khai báo subject một lần và có thể truy cập nó theo một cách ngầm định (implicitly) thông qua phong cách one-liners và một cách rõ ràng (explicitly) thông qua method được tạo ra.

Để hiểu rõ hơn về subject chúng ta cùng đi qua 3 vấn đề chính:

  1. Implicitly defined subject - Tạm dịch: subject được định nghĩa ngầm
  2. Explicitly defined subject - Tạm dịch: subject được định nghĩa rõ ràng
  3. One-liner syntax - Tạm dịch: Cú pháp phong cách một dòng

Implicitly defined subject - subject được định nghĩa ngầm

Tại sao lại dùng cụm từ implicitly defined subject vì đơn giản nó được thực hiện ngầm mà khiến người dùng ban đầu thấy nó có quá nhiều magic và khó hiểu. Nhiều khi bạn thấy ở đâu đó một đoạn code giống như sau:

RSpec.describe Array do it "should be empty array when first created" do expect(subject).to be_empty end
end

Kha khá các câu hỏi có thể đặt ra ở đây như subject là cái gì? subject chứa gì? Dùng subject như nào cho hợp lý? …

Ở ví dụ trên thì subject method trả về một instance của class Array vì vậy kết quả quả test trên là pass.

Giải thích: subject method sẽ tương ứng với giá trị trả về của Array.new hay []. be_empty sẽ tương ứng với kết quả trả về của Array#empty?

Từ ví dụ trên chúng ta đi qua định nghĩa:

Nếu đối số đầu tiên truyền vào một example group là một class thì một instance của class đó đều khả dụng trong các example của example group đó thông qua subject method. Có nghĩa là subject method sẽ trả về instance của class đó.

Những phần xử lý đó là chạy ngầm và tự động nên chúng ta mới sử dụng cụm từ implicitly defined subject - subject được định nghĩa ẩn.

Các example group lồng nhau

(Nếu các bạn chưa biết thì khi sử dụng describe, context trong rspec thì các bạn đã tạo ra một example group và it sẽ tạo ra một example.)

Các bạn chỉ cần nhớ giá trị subject method sẽ nhận giá trị của example group trong cùng, giống với câu "Phép vua thua lệ làng" đó.

class ArrayWithOneElement < Array def initialize(*) super unshift "first element" end
end RSpec.describe Array do describe ArrayWithOneElement do context "referenced as subject" do it "contains one element" do expect(subject).to include("first element") end end end
end
// KQ: Pass hết nhé

Chúng ta thấy có 2 group example là (1) RSpec.describe Array(2) describe ArrayWithOneElement vì (2) nằm bên trong (1) nên subject method bên trong những example của (2). Dễ dàng có thể thử được kết quả trả về của subject method là:

Do đó subject chắc chắn sẽ include "first element" rồi.

Các bạn cũng có thể đặt thêm một câu hỏi nữa: Vậy những example bên ngoài (2) thì subject method sẽ trả về gì nhỉ? Mình đoán kết quả sẽ là của (1) thôi hay subject = Array.new = []

Chứng minh thì rất dễ thôi hãy kiểm tra kết quả của đoạn code này nhé.

class ArrayWithOneElement < Array def initialize(*) super unshift 'first element' end
end RSpec.describe Array do describe ArrayWithOneElement do context 'referenced as subject' do it 'contains one element' do expect(subject).to include('first element') end end end it "should be empty when first created" do expect(subject).to be_empty end
end

Kết quả:

Explicitly defined subject - subject được định nghĩa rõ ràng

Đây là phần mô tả được trích từ relishapp.com :

"Use subject in the group scope to explicitly define the value that is returned by the subject method in the example scope.

...

A named subject improves on the explicit subject by assigning it a contextually semantic name. Since a named subject is an explicit subject, it still defines the value that is returned by the subject method in the example scope. However, it defines an additional helper method with the provided name. This helper method is memoized. The value is cached across multiple calls in the same example but not across examples.

We recommend using the named helper method over subject in examples."

Tạm dịch :

Sử dụng subject trong phạm vi group example để định nghĩa rõ ràng giá trị trả về bởi subject method trong phạm vi example.

Một subject được đặt tên cải thiện sự rõ ràng của subject bởi gán cho nó một cái tên phù hợp với ngữ cảnh. Khi một subject được đặt tên là một subject rõ ràng, thì nó vẫn định nghĩa giá trị được trả về cho subject method trong phạm vi example. Tuy nhiên, nó cũng định nghĩa thêm một helper method với tên được cung cấp và helper method này sẽ được ghi nhớ. Giá trị được cache giữa những lần gọi trong cùng một example và không cache giữa các example.

Qua mô tả thì có thể hiểu như sau:

  1. Trong phạm vi của example group (bên ngoài các example) các bạn sẽ sử dụng subject để chỉ rõ ràng giá trị mà subject method trả về.
RSpec.describe Array, "with some elements" do //example group subject { [1, 2, 3] } it "has the prescribed elements" do // a example expect(subject).to eq([1, 2, 3]) end
end

Trong ví dụ trên subject method sẽ trả về [1, 2, 3] thay vì [] như implicitly defined subject. Nếu các bạn cố tình khai báo subject bên trong một example thì sẽ không có tác dụng và rspec cũng không báo lỗi gì.

  1. Nếu khai báo như cách 1 chưa đủ rõ ràng thì chúng ta có thể đặt tên cho từng subject. Việc này sẽ giúp chúng ta làm 2 thứ một là gán giá trị trả về cho subject method, hai là tạo thêm một helper method với giá trị trả về giống subject method.
RSpec.describe Array, "with some elements" do //example group subject(:name) { [1, 2, 3] } it "has the prescribed elements" do // a example expect(name).to eq([1, 2, 3]) // Thay vì expect(subject).to eq([1, 2, 3]) end
end

Việc sử dụng subject một cách rõ ràng như vậy sẽ đỡ gây nhiều sự khó hiểu và sai lầm cũng như magic của subject. Explicitly example có rất nhiều cách dùng và đặc điểm được nhắc đến trong đây các bạn có thể tham khảo nhé.

Tips: một cách để các bạn quản lý và phán đoán được giá trị của subject sẽ là gì và bị biến đổi ra sao thì có thể sử dụng tư tưởng sau:

  • subject được khai báo bên trong sẽ có độ ưu tiên cao hơn bên ngoài (Phép vua thua lệ làng).
  • subject method của từng example group là không ảnh hưởng đến nhau.
  • example group bên trong không có subject thì sẽ lấy của example group bên ngoài nếu có.

Ví dụ:

class ArrayWithOneElement < Array def initialize(*) super unshift 'first element' end
end RSpec.describe Array do # example group 1 - EG1 subject(:name) { [] } describe ArrayWithOneElement do # example group 2 - EG2 subject(:name) { ['first element'] } context 'referenced as subject' do # example group 3 - EG3 it 'contains one element' do # example expect(name).to include('first element') # Vì EG3 không có subject nên dùng EG2, EG2 có độ ưu tiên cao hơn EG1 end end end it 'should be empty when first created' do # example expect(name).to be_empty #subject của EG1 end
end

One-liner syntax

Rspec hỗ trợ cú pháp trên một dòng để mô tả một expectation trên subject.

Ví dụ: Cách thông thường:

RSpec.describe Array do # example group 1 - EG1 it 'should be empty when first created' do # example expect(subject).to be_empty #subject của EG1 end
end

Phong cách một dòng :

RSpec.describe Array do it { is_expected.to be_empty }
end

còn những phần mô tả cho example như 'should be empty when first created' sẽ được tự động tạo ra.

Ngoài is_expected chúng ta có thể sử dụng với should như:

RSpec.describe Array do it { should be_empty }
end

Có một vài lưu ý :

  • Đây là tính năng chỉ khả dụng khi sử dụng gem rspec-expectations
  • Những examples sử dụng phong cách one-liner syntax không thể gọi trực tiếp từ command line với option --example
  • One-liner syntax chỉ hoạt động với non-block expectations (ví dụ: expect(obj)) và sẽ không hoạt động với block expectations (ví dụ: expect{ object })

Với cách viết này sẽ ngắn gọn hơn rất nhiều và thường thấy trong khi viết rspec cho model ở trong rails.

Kết luận

Qua những kiến thức được tổng hợp và giải thích theo ý hiểu của mình mong các bạn có thể hiểu được những kiến thức cơ bản về subject. Giúp bạn trả lời được những câu hỏi cơ bản nó là cái gì, tại sao nó lại như vậy và cách dùng cơ bản. Thanks for reading to my post!

Nguồn tham khảo

  1. Relish

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tìm hiểu cơ bản về Rspec helper methods

Helper methods. Trong khi sử dụng RSPEC, chắc hẳn các bạn sử dụng rất nhiều let và let!.

0 0 62

- vừa được xem lúc

Example groups Rspec - Part 1

Quảng cáo phương thuốc quý. Nếu các bạn đã chán ngấy với việc viết và nhìn những dòng code sau đây lặp đi lặp lại thì bài viết này là dành cho bạn.

0 0 24

- vừa được xem lúc

RSpec: sự khác nhau giữa Mocks và Stub

Sau một thời gian tìm hiểu và làm quen với Mocks và Stubs trong RSpec mình vẫn không thể phân biệt rõ ràng Mocks và Stubs nên thường dùng lẫn lộn giữa chúng. Ở chúng có những điểm tương đồng nhưng cũng có sự khác biệt nên sẽ có những trường hợp sử dụng tối ưu riêng.

0 0 103

- vừa được xem lúc

Better rspec

. Developer không chỉ code mà cần phải viết test đi kèm theo với Code. Trong ngôn ngữ Ruby, RSpec là một trong những testing framework được sử dụng phổ biến nhất.

0 0 32

- vừa được xem lúc

Viết Rspec như thế nào cho tốt?

Hồi mình mới sử dụng Rspec mình dường như chưa hiểu thực sự ý nghĩa của viết Unit Test (Rspec) nên chỉ viết cho có, hay chưa có tâm. Viết unit test để làm gì, mặc dù cũng có Google đọc các thể loại ch

0 0 56

- vừa được xem lúc

Ví dụ viết test RSpec và viết code Ruby on Rails

Bài viết này được lấy cảm hứng từ phần thuyết trình của cty S ở Ruby Kaigi 2021. Và để khởi động thì mình sẽ viết về 1 ví dụ unit test.

0 0 46