Hướng dẫn để viết Rspec tốt hơn
Bài đăng này đã không được cập nhật trong 3 năm
Trong thời gian mình mới tham gia vào dự án, điều mình cảm thấy bỡ ngỡ nhất là viết rspec. Dự án mình chủ yếu viết rspec ở model sao cho luôn được coverage 100%. Nếu có nhiều thời gian chúng ta có thể viết thêm rspec ở controller, service, decorator ...vv..vv nhưng việc viết rpsec cho model là vô cùng quan trọng và cần thiết. Tại sao lại như vậy? Vì 1 dự án luôn làm việc trong một nhóm người, hôm nay bạn viết hàm A1 trong model A nhưng hôm sau một người khác vào sửa hàm đó thì rspec sẽ giúp chúng ta kiểm soát được những thay đổi liên quan đến lấy và truy xuất dữ liệu. Việc viết rspec như thể bạn viết 1 hàm sau đó bạn giả lập dữ liệu và đi qua hàm đó với một kết quả mong đợi của bạn sau đó sẽ rspec sẽ so sánh kết quả đó với kết quả thực tế hàm đó trả về. Nếu trùng nhau thì coi như hàm đó đã pass. Điều này rất giống chúng ta viết các test case để kiểm thử. Một câu hỏi đặt ra là viết rspec thế nào cho tốt? Bài viết sẽ trình bày một số cách viết rspec để giúp cho những người mới bắt đầu viết rspec có thể tiết cận nó dễ dàng hơn
Mình xin giới thiệu sơ qua về cách cài đặt rspec
Cách cài đặt rspec
Khởi đầu tạo một app mới:
rails new myapp
Cài đặt RSpec: Ta mở gemfile và thêm gem rspec-rails gem vào nhóm :development, :test
group :development, :test do
gem 'byebug'
gem 'rspec-rails', '~> 3.4'
end
Sau đó install gem:
bundle install
sau đó chạy dòng lệnh bên dưới để thêm thư viện spec và các file liên quan, bao gồm file .rspec
myapp rails generate rspec:install
Để chạy rspec ta dùng các câu lệnh:
# Chỉ chạy rspec tại các model
bundle exec rspec spec/models
# Chạy rspec ở controller AccountsController
bundle exec rspec spec/controllers/accounts_controller_spec.rb
# Chạy rspec ở dòng 8 của controller AccountsController
bundle exec rspec spec/controllers/accounts_controller_spec.rb:8
Sau khi chạy chúng ta sẽ thấy: VD rspec đã đi qua 30 test case và tất cả đều pass
Cách viết rspec tốt hơn
- Làm thế nào để mô tả 1 method?
Hãy mô tả rõ ràng về method của bạn.Ví dụ theo tài liệu của Ruby quy ước sử dụng . hoặc :: để trước tên của method và sử dụng # khi đề cập đến 1 instance method của method đó. Ví dụ:
- Sử dụng context:
Bối cảnh(context) là một phương pháp mạnh mẽ để làm các test case của bạn rõ ràng. Giúp những người khác có thể mường tượng dễ hơn về hàm của bạn có xử lý những chức năng như thế nào. Để mô tả context, nên bắt đầu bằng từ When hoặc With
- Luôn giữ cho phần mô tả ngắn nhất có thể
Một mô tả không nên dài hơn 40 ký tự. Nếu điều này xảy ra, bạn nên chia nó sử dụng ngữ cảnh.
- Viết test tất cả các case có thể
Viết test rất tốt nhưng nếu bạn bỏ qua các trường hợp dễ gây ra lỗi thì việc test sẽ hoàn toàn vô ích. Nên điều quan trọng là phải test tất cả các trường hợp invalid. Ví dụ: viết test cho 1 action destroy tại controller
Có thể @consumption được tìm thấy, không được tìm thấy hoặc nó k thuộc quyền sở hữu? Vậy có ít nhất 3 trường hợp cần phải test
- Sử dụng subject
Sử dụng subject để gọi trực tiếp một hàm nào đó thay vì chúng ta viết rút gọn
- Sử dụng let và let!
Thay vì mỗi lần dùng một đối tượng nào đó ta lại sử dụng before để tạo ra đối tượng đó trong 1 context. Nhưng chúng ta có thể sử dụng let và let! giúp chỉ việc tạo ra 1 đối tượng có thể dùng nhiều lần trong 1 describe, có thể sử dụng ở nhiều context khác nhau. Nó sống khi bắt đầu describe và tự hủy khi kết thúc describe đó
- Chỉ tạo ra những data mà bạn cần
Để tránh với việc chạy chậm do tải dữ liệu quá lớn chúng ta chỉ nên tạo ra dữ liệu cần thiết đủ để sử dụng
- Sử dụng factory để tạo dữ liệu
Sử dụng factory để tạo data giúp chúng ta dễ dàng kiểm soát dữ liệu test của mình. Đồng thời sử dụng chúng rất thuận tiện để tạo dữ liệu
- Shared example
Viết test rất tốt và thuận tiện giúp chúng ta kiểm soát mọi thứ dễ dàng hơn, nhưng một ngày bạn nhìn lại có quá nhiều đoạn code trùng nhau. Sử dụng shared example để DRY đoạn code test của bạn
Ví dụ: Kiểm tra hợp lệ của password: Đầu tiên ta tạo 1 shared_example ứng với ngữ cảnh password không hợp lệ
shared_examples "validate password complexity" do |input, result|
before do
admin.update(password: input)
admin.valid?
end
it{expect(admin.errors.messages[:password]).to eq(result)}
end
Sau đó chúng ta viết tất cả các trường hợp password không hợp lệ, tất cả các case sẽ đi qua shared_example và cho ta kết quả.
Sử dụng it_behaves_like "Tên case", "giá trị không hợp lệ", "kết quả mong đợi khi đi qua shared_example". Ví dụ
describe "#password_complexity" do
let(:admin){FactoryGirl.create :admin}
context "when password only digit" do
it_behaves_like "validate password complexity", "11111111",
[I18n.t("devise.passwords.password_complexity")]
end
context "when password only character" do
it_behaves_like "validate password complexity", "aaaaaaaa",
[I18n.t("devise.passwords.password_complexity")]
end
context "when password only special character" do
it_behaves_like "validate password complexity", "@@@@@@@@@@",
[I18n.t("devise.passwords.password_complexity")]
end
context "when password has digit, character and special character" do
it_behaves_like "validate password complexity", "Aa@123456", nil
end
context "when password has digit and character" do
it_behaves_like "validate password complexity", "framgia4013", nil
end
context "when password has digit and special character" do
it_behaves_like "validate password complexity", "&!@123^456", nil
end
context "when password has character and special character" do
it_behaves_like "validate password complexity", "@lohadance", nil
end
end
- Test tự động với Guard
Chạy tất cả các test khi bạn thay đổi code của app, điều nay gây mất thời gian và ảnh hưởng đến flow của bạn. Với Guard, Nó giúp tự động chạy các bộ kiểm tra khi bạn update rspec tại model, hay controller hay bất cứ đâu. sử dụng câu lệnh:
bundle exec guard
Hi vọng bài viết sẽ giúp ích cho bạn để làm quen với rspec tốt hợn
**Nguồn tham khảo
https://www.sitepoint.com/learn-the-first-best-practices-for-rails-and-rspec/
All rights reserved