Hướng dẫn để viết Rspec tốt hơn

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

Screenshot from 2016-11-26 09:25:10.png

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ụ:

Screenshot from 2016-11-26 09:33:54.png

  • 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

Screenshot from 2016-11-26 09:39:04.png

  • 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.

Screenshot from 2016-11-26 09:41:39.png

  • 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

Screenshot from 2016-11-26 09:45:27.png

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

Screenshot from 2016-11-26 09:48:23.png

  • 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

Screenshot from 2016-11-26 09:53:04.png

Screenshot from 2016-11-26 09:52:00.png

  • 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 đó

Screenshot from 2016-11-26 09:55:51.png

  • 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

Screenshot from 2016-11-26 10:01:00.png

  • 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

Screenshot from 2016-11-26 10:03:20.png

  • 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", "[email protected]", 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", "&[email protected]^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/

http://betterspecs.org/