Rspec: Định nghĩa matcher riêng
Các matcher được xây dựng sẵn trong Rspec rất tốt nhưng không đầy đủ. May mắn là chúng ta có thể tạo ra được những matcher riêng, và đương nhiên chúng ta có thể sử dụng chúng trên toàn bộ project. Để chứng minh một cách rõ hơn vì sao chúng ta cần phải định nghĩa một matcher riêng, chúng ta sẽ sử ...
Các matcher được xây dựng sẵn trong Rspec rất tốt nhưng không đầy đủ. May mắn là chúng ta có thể tạo ra được những matcher riêng, và đương nhiên chúng ta có thể sử dụng chúng trên toàn bộ project. Để chứng minh một cách rõ hơn vì sao chúng ta cần phải định nghĩa một matcher riêng, chúng ta sẽ sử dụng lớp Persion đại diện cho người sử dụng ứng dụng. Chúng ta sẽ bắt đầu theo hướng TDD(Test Driven Development). Yêu cầu như sau:
- Có field role với giá trị default là user.
- Có fiend verified với giá trị default là false.
- Có thể nâng cấp lên admin và tự động verify.
Chúng ta có thể tạo ra một đối tượng trong Ruby đơn giản và focus vào việc nâng cấp role lên admin. Đặt tên của nó là upgrade_to_admin:
require 'spec_helper' describe Person do it 'sets person role to user by the default' do person = Person.new expect(person.role).to eq('user') end it 'makes the person not verified by the default' do person = Person.new expect(person.verified).to eq(false) end describe '#upgrade_to_admin' do it 'upgrades to admin' do person = Person.new person.upgrade_to_admin expect(person.role).to eq('admin') expect(person.verified).to eq(true) end end end
Chúng ta hãy tạo ra lớp Persion thoả mãn yêu cầu ở trên:
class Person attr_reader :role, :verified def initialize @role = 'user' @verified = false end def upgrade_to_admin @role = 'admin' @verified = true end end
Custom matcher
Hãy tập trung vào việc liệu đối tượng đã có role chưa. Chúng ta sẽ xây dựng custom matcher cho đoạn test sau:
expect(person.role).to eq('user')
Vì chúng ta sử sử dụng TDD để tiếp cận nên đầu tiên chúng ta sẽ viết như sau:
expect(person).to be_user
Nếu làm như thế này chúng ta sẽ thấy có quá nhiều cú pháp xuất hiện. Đây là một ví dụ không tốt về việc tạo ra custom matcher nhưng là một ví dụ đơn giản phục vụ cho mục đích của bài viết, đó là giới thiệu cách tạo ra custom matcher và vì vậy hãy đừng để ý đến vấn đề này. Tạo tập tin mới trong thư mục spec/support/ và đặt tên là matchers.rb hoặc bất cứ cái gì bạn muốn. Chúng ta sẽ sử dụng một chút metaprogramming ở đây bằng cách sử dụng RSpec DSL:
RSpec::Matchers.define :be_user do |expected| match do |actual| expect(actual.role).to eq('user') end end
Chúng ta có 2 biến số ở đây:
- expected là giá trị được truyền cho matcher. Vì chúng ta không truyền tham số nào cho be_user nên nó chứa giá trị nil.
- actual là tham số truyền vào expect
Bước cuối cùng là require matchers.rb. Để thực hiện việc này, hãy chỉnh sửa spec_helper.rb hoặc rails_helper.rb và sử dụng require:
require 'support/matchers'
Bây giờ hãy chạy lại test của bạn.
Update các mô tả mặc định
Chúng ta có thể đơn giản hoá rspec chỉ còn 1 dòng như sau:
it { expect(Person.new).to be_user }
Bây giờ chúng ta chạy rspec với flag --format documentation. Sẽ nhận được output đơn giản là:
Person
should be user
Chúng ta có thể control nó. Chúng ta cần update lại custom matcher, và sử dụng description block:
RSpec::Matchers.define :be_user do |expected| match do |actual| expect(actual.role).to eq('user') end description do "have user role and be unverified" end end
Chạy lại rspec và nhận được kết quả khác với lúc trước:
Person should have user role and be unverified
Điều này sẽ làm cho mô tả rspec có ý nghĩa hơn.
Kết luận
Trên đây chỉ là ví dụ đơn giản giúp bạn biết đc cách thức hoạt động và có thể tạo ra custom matcher cho riêng mình. Tuy nhiên việc sử dụng nó cũng cần phải đúng lúc đúng chỗ. Ngoài ra chúng ta có thể tìm hiểu thêm về kỹ thuật TDD. Nó sẽ giúp ích rấ nhiều về lối tư duy cũng như việc code dễ dàng hơn. Tài liệu về TDD: RSpec & Test Driven Development Refereces: http://pdabrowski.com/blog/ruby-on-rails/testing/rspec-custom-matchers/