Tìm hiểu gem shoulda matchers trong viết Rspec

1.Giới thiệu

Viết sử dụng Rspec để viết test cho các dự án Rails là hết sức quan trọng. Đối với những dự án lớn thì việc viết test sẽ vô cùng phức tạp và mất thời gian.Shoulda Matchers cung cấp cách viết 1 dòng lệnh Rspec dùng để test các chức năng của Rails. Nó giúp bạn viết test 1 cách ngắn gọn, rõ ràng và ít gặp lỗi hơn. Sử dụng Shoulda Matchers giúp cho việc viết rspec đơn giản, ngắn gọn và thú vị hơn. Bài viết sẽ cho bạn 1 cái nhìn để so sánh giữ việc viết test dùng Should Matchers với viết Rspec thông thường.

2.Cài đặt

Thêm vào Gemfile

group :test, :development do
  gem 'rspec-rails'
  gem 'shoulda-matchers'
end

Sau đó chạy bundle

3.Hướng dẫn sử dụng

Shoulda Matchers cùng cấp cho ta rất nhiều phương thức dùng để test ví dụ:

ActiveModel:

  • have_secure_password
  • validate_absence_of
  • validate_acceptance_of
  • validate_confirmation_of
  • validate_exclusion_of
  • validate_inclusion_of
  • validate_length_of
  • validate_numericality_of
  • validate_presence_of

Ví dụ : Below is an RSpec unit test for an ActiveModel. Each named test goes through four distinct phases: setup, exercise, verify, and teardown. The first named test ensures that you can't create a valid Contact record without a first_name attribute; the second test makes sure each Contact record has a unique email address, and the third enforces a minimum password length requirement.

Dưới đây là ví dụ viết RSpec cho một ActiveModel. Ví dụ yêu cầu khi tạo 1 Contact:

  • Phải có first_name.
  • email không được trùng lặp.
  • password phải có ít nhất 10 ký tự
describe Contact do
  it "is invalid without a firstname" do
    contact = Contact.create(first_name: nil, email: "[email protected]", password: "empirestatebuilding")

    contact.valid?

    expect(contact.errors[:firstname]).to include("can't be blank")
  end

  it "is invalid with a duplicate email address" do
    Contact.create(first_name: "Ted", email: "[email protected]", password: "empirestatebuilding")
    contact = Contact.new(first_name: "Robin", email: "[email protected]", password: "montrealcanadiens")

    contact.valid?

    expect(contact.errors[:emal]).to include("has already been taken")
  end

  it "is invalid without a minimum password length" do
    contact_one = Contact.create(first_name: "Ted", email: "[email protected]", password: "empire")

    contact_one.valid?

    expect(Contact.errors[:password]).to include("password is too short")
  end
end

Như bạn thấy ở trên, với cú pháp bình thường bạn phải mất rất nhiều dòng code để có thể kiểm tra được 3 logic ở trên. Nhưng với Shoulda Matchers bạn chỉ cần phải dùng 3 dòng

describe Contact do
  it { is_expected.to validate_presence_of(:firstname) }
  it { is_expected.to validate_uniqueness_of(:email) }
  it { is_expected.to validate_length_of(:password).is_at_least(10) }
end

ActiveRecord :

  • accept_nested_attributes_for
  • belong_to
  • define_enum_for
  • have_and_belong_to_many
  • have_db_column
  • have_db_index tests
  • have_many
  • have_one
  • have_readonly_attribute
  • serialize
  • validate_uniqueness_of

Một ví dụ về test mối quan hệ has_many : Cú pháp Rspec:

describe Contact do
  it "can have many phone numbers" do
    contact = Contact.reflect_on_association(:phones)
    expect(contact.macro).to eql(:has_many)
  end
end

Cú pháp Rspec + Shoulda Matchers :

describe Contact do
  it { is_expected.to have_many(:phones).dependent(:destroy) }
end

ActionController : Controller thường ít khi được viết test nhưng Shoulda Mathcher cũng cung cấp rất đầy đủ các phương thức để ta có thể viết test 1 cách dễ dàng và ngắn gọn nhất.

  • filter_param
  • permit tests
  • redirect_to
  • render_template
  • render_with_layout
  • rescue_from tests
  • respond_with tests
  • route
  • set_session
  • set_flash
  • use_after_action
  • use_around_action
  • use_before_action tests

Khi viết test cho 1 controller ta thường quan tâm tới :

  • Trạng thái trả về của phương thức
  • Controller có render template mong muốn không?
  • Controller có render layout mong muốn không?
  • Controller có tạo flash messages hay không?
  • Thông tin có thay đổi hay bị xóa bỏ hay không?
  • Controller có chuyển đến url mới không? Ví dụ :
describe ContactsController do
  describe "GET #index" do
    context "when user is logged in" do
      with :user
      before do
        sign_in user
        get :index
      end
      it { is_expected.to respond_with :ok }
      it { is_expected.to render_with_layout :application }
      it { is_expected.to render_template :index }
    end
    context "when user is logged out" do
      before do
        get :index
      end
      it { is_expected.to redirect_to new_session_path }
      it { is_expected.to set_the_flash(:warning).to("Please log in.") }
      it { is_expected.to set_session(:return_to).to(contacts_path) }
    end
  end
end

4.Kết luận

Thông qua các ví dụ trên ta có thể dễ dàng nhận thấy được sự đơn giản, tiện lợi trong việc sử dụng Shoulda mathcher trong việc viết Rspec so với cú pháp thông thường. Bạn có thể tìm hiểu nhiều ví dụ của Shoulda mathchers và thông qua đó thực hành, áp dụng vào project của bạn tại :

https://github.com/thoughtbot/shoulda-matchers
Chúc các bạn thành công !!!