Rails 实战 - RSpec
source link: http://blog.danthought.com/programming/2015/07/05/rails-in-action-rspec/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Ruby 社区一直都喜欢测试驱动开发,Rails 框架内置就有测试模块,本文将介绍如何利用 RSpec 来做单元测试,当然 RSpec 也可以用来做集成测试。
Rails 和 RSpec
Rails 项目中使用 RSpec,在 Gemfile 加入如下配置:
group :test do
gem 'rspec-rails', '~> 3.1.0'
gem 'factory_girl_rails'
end
安装 Gems:
$ bundle install
初始化 spec 目录,spec 目录下会生成文件 spec_helper.rb,此文件将 RSpec 和 Rails 结合起来,运行 RSpec 测试的时候加载 Rails 项目等等这些:
$ rails generate rspec:install
Factory Girl
上一步配置 Gemfile 文件的时候,还声明了 factory_girl_rails,Factory Girl 作用是生成测试需要的数据,在测试很多问题的时候,都需要预先在数据库中填充一些数据,测试完一个单元,还应该清除数据库中的数据,保证下一个单元的测试不被这些遗留数据所影响,我们应该为每一个测试中要用到的 Model,在 spec/factories 编写一个 Factory:
# spec/factories/category.rb
FactoryGirl.define do
factory :category do
language "en"
name "A Category"
factory :food_category do
is_food true
end
factory :exercise_category do
is_food false
end
end
end
# spec/factories/exercise.rb
FactoryGirl.define do
factory :exercise do
language "en"
name "A Exercise"
association :category, :factory => :exercise_category
end
end
测试 Models
在 spec/models 中编写针对 Models 的业务逻辑测试。
每一个锻炼都有一个分类,当分类下面的锻炼不为零的时候,分类是不能够被删除的。
接着我们来编写测试 spec/models/category_spec.rb 来保证上面这个需求:
require "spec_helper"
describe Category, :type => :model do
it "can not remove category have exercises" do
category = FactoryGirl.create(:exercise).category
expect(category.destroy).to be false
expect(category.errors.full_messages.first).to eql("Cannot destroy category when its exercises not zero")
end
end
终端中运行测试,我们还没有实现这样的业务逻辑,肯定会报错:
$ bin/rspec spec/models/category_spec.rb
通过 before_destory 实现相应的业务逻辑,再次运行上面的测试,就会看到测试通过:
class Category < ActiveRecord::Base
has_many :foods
has_many :exercises
validates :name, :language, :presence => true
validates :name, :length => { :maximum => 23 }
before_destroy do |category|
if category.is_food
unless category.foods.count == 0
errors[:base] << I18n.t("errors.not_zero", :scope => :admin, :parent => I18n.t("models.category", :scope => :activerecord).downcase, :children => I18n.t("foods", :scope => :admin).downcase)
false
end
else
unless category.exercises.count == 0
errors[:base] << I18n.t("errors.not_zero", :scope => :admin, :parent => I18n.t("models.category", :scope => :activerecord).downcase, :children => I18n.t("exercises", :scope => :admin).downcase)
false
end
end
end
end
测试 Controllers
在 spec/controllers 中编写针对 Controllers 的业务逻辑测试。
输入正确的用户名和密码就可以登录到管理页面,否则就不可以
接着我们来编写测试 spec/controllers/sessions_controller_spec.rb 来保证这个上面这个需求:
require "spec_helper"
describe SessionsController, :type => :controller do
context "post create" do
it "login as admin" do
admin = FactoryGirl.create(:admin)
post :create, { :username => admin.username, :password => admin.password }
expect(response).to redirect_to(admin_root_path)
end
it "cannot login as normal user" do
user = FactoryGirl.create(:user)
post :create, { :username => user.username, :password => user.password }
expect(response).to be_success
expect(response.status).to eq(200)
expect(flash[:alert]).to eql("Invalid username or password")
end
end
end
终端中运行测试,我们还没有实现这样的业务逻辑,肯定会报错:
$ bin/rspec spec/controllers/sessions_controller_spec.rb
通过 create 实现相应的业务逻辑,再次运行上面的测试,就会看到测试通过:
class SessionsController < ApplicationController
layout false
def create
user = User.find_by_username_and_password_and_admin(params[:username], params[:password], true)
if user
session[:login] = user.id
redirect_to admin_root_path
else
flash.now[:alert] = I18n.t("session.error")
render :new
end
end
end
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK