Rspec: Định nghĩa matcher riêng
Bài đăng này đã không được cập nhật trong 6 năm
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 chobe_user
nên nó chứa giá trịnil
.actual
là tham số truyền vàoexpect
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/
All rights reserved