12

Rails 实战 - RSpec

 3 years ago
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.
neoserver,ios ssh client

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK