Test against Controller(fabrication and faker gem)

This article is showing how to test VideosController中 show and search :

The steps of a test is:

  1. set up
  2. perform action
  3. verify against the result

VideosController:

class VideosController < ApplicationController
before_filter :require_user
  layout "application"

def index
@categories = Category.all
@videos = Video.all
end
  def show
@video = Video.find_by(id: params[:id])
end
  def search
@match_videos = Video.search_by_title(params[:search_item])
end
end

VideosControllers rspec file:

require 'spec_helper'
describe VideosController do 
# there are two thing is executed in show method
# 1. setup @video
# 2. render show.erb.html
describe 'GET show' do
it 'set @video' do
video = Fabricate(:video)
get :show, id: video.id
expect(assigns(:video)).to eq(video)
end
end
end

其中 assigns(:video) 就是我們上一行用 get :show, id: video.id 送出http request後, 在原本 show method中得到的 @video

@video 這個instance variable, 將會等於一開始我們用fabricator設定的video

使用fabricator而不用 video = Video.create(name: ‘south park' , description: ‘funny video'.....) 是因為如果這筆資料總共需要設定10個欄位(name, description, category….), 而其中很多個欄位是必須不能空白的的(validates_presence_of :name, :description…) , 往後在每一個測試中需要用的video都要重新設定一次 video = Video.create(name: ‘south park' , description: ‘funny video'.....) 會非常冗長且麻煩, 如果能把這筆資料用其他方式先預存起來, 這樣我們就可以免去重複宣告的步驟

用的gem有 fabrication and faker

fabrication 的用處是幫我們創造ActiveRecord Model

Faker會自動幫我們創造根據需求相對應的字串

所以在 spec/fabricators/video_fabricator.rb 中(add in Gemfile and execute bundle install before)

Fabricator(:video) do
title { Faker::Lorem.words(5)}
description { Faker::Lorem.paragraph(2)}
end

測試檔案只這樣寫會發生的問題是, VideosController中所有的動作都需要有使用者登入(情境是使用者登入後才能看到裡面的頁面), 因此我們要先創造一個環境是在show之前有使用者登入(即session中有:user_id)

require 'spec_helper'
describe VideosController do
describe "GET show" do
context 'with authenticated users' do
before do
session[:user_id] = Fabricate(:user).id
end
it 'sets @video' do
video = Fabricate(:video)
get :show, id: video.id
expect(assigns(:video)).to eq(video)
end
end
end
it 'search the right video'
end

再創造一個 spec/fabricators/user_fabricator.rb

Fabricator(:user) do
email { Faker::Internet.email }
password { 'pasword' }
full_name { Faker::Name.name }
end

就可以順利通過測試了!

當我們連到show route時會做的第二個動作是 render view/show.erb.html, 測試如下

describe VideosController do
describe "GET show" do
context 'with authenticated users' do
before do
session[:user_id] = Fabricate(:user).id
end

it 'renders the show template' do
video = Fabricate(:video)
get :show, id: video.id
expect(response).to render_template :show
end
end
end
end

但render的步驟不是我們寫在 show當中的, 是rails預測做的動作, 因此我們沒有去測試的必要

再加上未登入的使用者會redirect_to到首頁, 就可以免去這個context, 併入第一個測試的說明中, 再加上沒有登入的redirect_to測試

describe VideosController do
describe "GET show" do
it 'sets @video for authenticated user' do
session[:user_id] = Fabricate(:user).id
video = Fabricate(:video)
get :show, id: video.id
expect(assigns(:video)).to eq(video)
end

it 'redirects the user to the sign in page for unahthenticated users' do
video = Fabricate(:video)
get :show, id: video.id
expect(response).to redirect_to sign_in_path
end
end
end

最後再測試search function

describe VideosController do
# omit for brevity
describe 'Post search' do
it 'sets @results for authenticated users' do
session[:user_id] = Fabricate(:user).id
futurama = Fabricate(:video, title: 'Futurama')
get :search, search_term: 'rama'
expect(assigns(:results)).to eq([futurama])
end
it 'redirects to sign in page for the unahthenticate users' do
futurama = Fabricate(:video, title: 'Futurama')
get :search, search_term: 'rama'
expect(response).to redirect_to sign_in_path
end
end
end

到此就完成了這個controller的測試, 這邊我們學到如何測試一個controller以及使用 fabricationfaker 來替我們創造ActiveRecord model和簡單的創造字串, 用這些技巧來讓測試的檔案更簡潔乾淨

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.