Thực hành viết rspec trong rails

Bài viết này nhằm giới thiệu tổng quan và cách cài đặt rspec trong rails. Những nội dung chính trong bài:

  • Setup rspec
  • Tạo factory dử dụng Factory Girl Rails và Faker
  • Viết Model specs
  • Viêt Controller specs
  • Feature specs

Tạo một rails application

Chạy câu lệnh sau để tạo một application mới có tên là myapp rails new myapp

Cài đặt RSpec

Trong Gemfile thêm vào gem "rspec-rails" vào nhóm development, test

group :development, :test do
  gem "byebug", platform: :mri
  gem "rspec-rails", "~> 3.4"
end

Chạy lệnh bundle để cài đặt RSpec, tiếp theo bạn chạy câu lệnh rails generate rspec:install với câu lệnh này sẽ tự động thêm thư mục spec và một vài file với duôi lài .rspec

Shoulda-Matchers và Database Cleaner

Mở file Gemfile và thêm vào test group hai gemshoulda-matcherdatabase_cleaner

 group :test do
  gem 'shoulda-matchers', '~> 3.0', require: false
  gem 'database_cleaner', '~> 1.5'
end

chạy lệnh bundle

Cấu hình Shoulda-Matchers

Shoulda-Matcher cung cấp match one-line tới RSpec để sử dụng trong test các chức này năng của Rails mà chúng ta sẽ thấy ngay đây thôi. Mở spec/rails_helper.rb và thêm đoạn code sau

require 'shoulda/matchers'
Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

Cấu hình Database Cleaner

Database Cleaner cung cấp cách để dọn dẹp cơ sở dữ liệu của bạn trong Ruby giữa chạy thử nghiệm. Nó đảm bảo làm sạch trạng thái cho testing.

để tích hợp database_cleaner chỉ cần chỉnh lại trong file rails_helper

config.use_transactional_fixtures = false

Tạo một thư mục mới gọi là support trong thư mục spec. Trong thư mục này tạo một file có tên là database_cleaner.rb và paste đoạn code sau vào

RSpec.configure do |config|

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, :js => true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
end

Vậy là database_cleaner đã được thiết lập. Và từ đây nó sẽ dọn dẹp database mỗi làn chạy unitest

Capybara Setup

Capybara là một framework tự động sử dụng để tạo chức năng test mô phỏng lại các users sử dụng và tương tác với ứng dụng như thế nào. Thêm gem capybara vào test group development, :test trong Gemfile và chay bundle

Mở spec_helper.rb và thêm require sau

require 'capybara/rspec'

Cài đặt Faker và Factory Girl

Faker được dùng để tạo dữ liệu random cho việc test của chúng ta. bạn sẽ nhìn thấy nó với factory_girl_rails. thêm gem faker trong :test trong test group

    gem "faker"

Tiếp đén Factory Girl cho phép bạn tạp object cần thiết khi test. kết hợp với faker bạn có thể tạo random một object có random dữ liệu khi test thay vì chỉ có một giá trị default.

Tạo một Factory

Tạo một thư mục tên là spec factories trong folder spec. tiếp theo thêm file sau model của bạn. Ví dụ contacts.rb

FactoryGirl.define do
  factory :contact do
    full_name     { Faker::Name.name }
    email         { Faker::Internet.email }
    phone_number  { Faker::PhoneNumber.phone_number }
    address       { Faker::Address.street_address }
  end
end

Trong đoạn code trên bạn có thể thấy cách mà tạo ra một dữ liệu cố định sử dụng factory_girl_railsfaker

Model Specs

Muốn hắc chắn dữ liệu trong factory mà tạo ở trên là một valid modlel, tạo một file trong modle/spec:

require 'rails_helper'

RSpec.describe Contact, type: :model do
  it "has a valid factory" do
    expect(contact).to be_valid
  end
end

chạy lệnh rspec spec/modle/contact_spec.rb nó sẽ thông báo có một lỗi xảy ra vì không có model contact. Giờ chúng ta sẽ tạo một model Contact với các thuộc tính cơ bản bằng câu lệnh sau trong terminal rails g model Contact full_name:string email:string phone_number:integer address:text rake db:migrate Chúng ta sẽ sử dụng shoulda-natchers bao bên ngoài model test để validate presence của full_name, phone_number và address.

  describe Contact do
    it { is_expected.to validate_presence_of(:full_name) }
    it { is_expected.to validate_presence_of(:email) }
    it { is_expected.to validate_presence_of(:phone_number) }
    it { is_expected.to validate_presence_of(:address) }
  end

chạy lại câu lệnh rspec trên bạn sẽ thấy nó fail. Để nó pass qua hết chỉ cần thêm validates_presence_of :full_name, :email, :phone_number, :address và trong contact.rb và chạy lại lần nữa, nó sẽ pass qua hết.

Controller Spec

Đầu tiên controller spec sẽ kiểm tra một new contact được created với các thuộc tính có giá trị hợp lệ. Trong terminal gõ lệnh sau để tạo controller cho contact rails g controller Contacts với câu lệnh này cũng tự động sinh ra nhưng folder cần thiết cho controller spec. Lần này để viết spec test create action trong file spec/controllers/contacts_controller_spec.rb

require 'rails_helper'

RSpec.describe ContactsController, type: :controller do

  describe "POST #create" do
    context "with valid attributes" do
      it "create new contact" do
        post :create, contact: attributes_for(:contact)
        expect(Contact.count).to eq(1)
      end
    end
  end
end

Tương tự như viết spec modlel chạy câu lệnh rspec spec/controllers/contacts_controller_spec.rb sẽ fail vì không có route thích hợp với controller. Vì thế bạn cần thêm resources :contacts vào file routes.rb chạy lại câu lệnh trên một lần nữa nhưng vẫn sẽ fail với error The action 'create' could not be found for ContactsController. do vậy phải thêm đoạn code sau vào contacts_contact.rb

 def new
   _@contact = Contact.new
 end

 def create
   _@contact = Contact.new contact_params
     if _@contact.save
       redirect_to @contact
     else
       render :new
     end
   end
 end

  private
  def contact_params
    params.require(:contact).permit :full_name, :email, :phone_number, :address
  end

lần này bạn chạy lại lệnh rpec và nó sẽ pass

Feature Spec

Feature Spec là test ở mức cao. làm việc trên application và chắc chắn rằng các thành phần của app đều hoạt động. kiểu test này sẽ đứng ở góc đọ user

để có cái nhìn tổng quát bạn sẽ viết một spec để kiểm tra việc tạo ra một new contact. Sử dụng gem Capybara, spec sẽ điền vào form các giá trị hợp lệ và show pasge sẽ hiển thị ra những gì chúng ta mong đợi sau khi tạo contact xong.

Tạo folder và file sau spec/features/contacts/create_spec.rb và thêm đoạn code sau vào:

require 'rails_helper'

RSpec.feature "Contact", :type => :feature do
  scenario "Create a new contact" do
    visit "/contacts/new"

    fill_in "Full name", :with => "My Name"
    fill_in "Email", :with => "[email protected]"
    fill_in "Phone number", :with => "123456789"
    fill_in "Address", :with => "34, Allen Way, OA"

    click_button "Create Contact"

    expect(page).to have_text("My Name")
  end
end

Các spec test trên nếu show page có chữ My Name, đó là giá trị được fill vào full_name. Chạy spec này và nó sẽ fail.

Đầu tiên phải thêm một show action vào controller:

def show
  _@contact = Contact.find params[:id]
end

Tiếp theo lần lượt tạo các file sau new.html.erb, _form.html.erb, show.html.erb và fill đoạn code tương ứng

***app/views/contacts/_form.html.erb***

<%= form_for(@contact) do |f| %>
  <% if @contact.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@contact.errors.count, "error") %> prohibited this contact from being saved:</h2>

      <ul>
      <% @contact.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :full_name %><br>
    <%= f.text_field :full_name %>
  </div>
  <div class="field">
    <%= f.label :email %><br>
    <%= f.text_area :email %>
  </div>
  <div class="field">
    <%= f.label :phone_number %><br>
    <%= f.text_area :phone_number %>
  </div>
  <div class="field">
    <%= f.label :address %><br>
    <%= f.text_area :address %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

***app/views/contacts/new.html.erb***

<h2>Create new contact</h2>

<%= render 'form' %>

***app/views/contacts/show.html.erb***

<p id="notice"><%= notice %></p>

<p>
  <strong>Full Name:</strong>
  <%= @contact.full_name %>
</p>

<p>
  <strong>Email:</strong>
  <%= @contact.email %>
</p>

<p>
  <strong>Phone Number:</strong>
  <%= @contact.phone_number %>
</p>

<p>
  <strong>Address:</strong>
  <%= @contact.address %>
</p>

chạy rspec và nó sẽ pass

Kết luận

Bài viết này mang tính chất giới thiệu về RSpec, cách cài đặt cũng như một số cách sử dụng cơ bản. Đây chỉ là những bước cơ bản đầu tiên, Và từ đây bạn có thể khám phá nhiều giá trị và lợi ích của rspec

All Rights Reserved