Introduction
RSpec 是 Ruby 中的一个测试框架,相比默认的测试框架 Test::Unit
而言,代码读起来更 Humanize
Install
在 Gemfile 中添加
group :development, :test do # rspec-rails 依赖 rspec,因此会自动安装 rspec # gem 'rspec' gem 'rspec-rails'end
然后运行 bundle install
,最后使用 rails g rspec:install
初始化。
Run
# 运行所有测试文件bundle exec rspec# 运行目录下的所有测试文件bundle exec rspec spec/routing# 运行单个测试文件bundle exec rspec spec/routing/root_routing_spec.rb
也可以使用 Guard 自动运行测试文件
Routing Specs
Routing Spec 用来测试 config/routes.rb
的编写是否符合预期:
通过访问 URL 能否抵达期望的 controller
和 action
,参数是否被正确的解析,通常使用 route_to(controller: '#', action: '#', param1: '#', param2: '#')
来测试。 测试某条路由是否能访问,通常使用 be_routable
来测试。
相关的测试文件放在 spec/routing
下。
Tips :
指定 URL 时可以直接指定路径 /sign_in
,也可以使用命名路由 sign_in_path
指定预期时可以使用 Hash
{ controller: '#', action: '#' }
,也可使用简写 controller#action
代码示例
sessions_spec.rb
require 'rails_helper'RSpec.describe 'sessions#new' do path = get sign_in_path expect(path).to route_to(controller: 'sessions', action: 'new') end it 'routes /sign_in via post to sessions#create' do path = post sign_in_path expect(path).to route_to(controller: 'sessions', action: 'create') end it 'routes /sign_out via delete to sessions#destroy' do path = delete sign_out_path expect(path).to route_to(controller: 'sessions', action: 'destroy') end it 'cannot routes /sign_out via get' do expect(get: sign_out_path).not_to be_routable endend
Model Specs
相关测试文件放在 spec/models
下
user_spec.rb
require 'rails_helper'RSpec.describe User, type: :model do it 'generate a name if no name provided' do user = User.create!( account: 'user1', password: 'user1', password_confirmation: 'user1' ) expect(user.name).to eq(user.account) endend
post_spec.rb
require 'rails_helper'RSpec.describe Post, type: :model do it 'has correct relationship' do post1 = Post.create!(title: 'Post 1', slug: 'post-1', body: 'Post 1') post2 = Post.create!(title: 'Post 2', slug: 'post-2', body: 'Post 2') post3 = Post.create!(title: 'Post 3', slug: 'post-3', body: 'Post 3') expect(post1.prev).to be_nil expect(post1.next).to eq(post2) expect(post2.prev).to eq(post1) expect(post2.next).to eq(post3) expect(post3.prev).to eq(post2) expect(post3.next).to be_nil expect(Post.recent).to eq([post3, post2, post1]) end it 'is replyable' do post = Post.create!(title: 'Replyable post', slug: 'replyable-post', body: 'Replyable post') reply1 = post.replies.create(author: 'reply1', email: 'reply1@reply.com', body: 'Reply 1') reply2 = post.replies.create(author: 'reply2', email: 'reply2@reply.com', body: 'Reply 2') expect(post.replies).to eq([reply1, reply2]) endend
Controller Specs
用来测试 controller
的行为是否符合预期,比如:
是否按照预期进行了 redirect_to=,使用 =redirect_to
测试 是否 render
了预期的 template=,使用 =render_template
测试 是否设置了预期的 cookie=,使用 =resonse.cookie
是否返回了预期的 http
状态码,使用 response.status
或者 has_http_status
测试
相关的测试文件放在 spec/controllers
下
posts_controller_spec
require 'rails_helper'RSpec.describe PostsController, type: :controller do describe 'Get #index' do it 'return success' do get :index expect(response).to be_success expect(response).to have_http_status(200) end it 'render the index template' do get :index expect(response).to render_template(:index) end endend
匿名 Controller
使用 controller
创建一个继承自当前所 describe
的 controller
的匿名 controller=,使用 =controller(BaseController)
可指定父类,通常用来测试异常。
require 'rails_helper'RSpec.describe ApplicationController, type: :controller do describe 'Get #index' do it 'return a 202 status code' do expect(get :index).to have_http_status(202) end it 'return text' do get :index expect(response.body).to eq('application#index') end endend
Helper Specs
使用 helper
来访问定义在 helper
中的方法,比如:
module ApplicationHelper def gravatar_url_for(email, size = 30, options = {}) digest = Digest::MD5.hexdigest(email.downcase) "http://gravatar.com/avatar/#{digest}.png?s=#{size}" endend
require 'rails_helper'RSpec.describe ApplicationHelper, type: :helper do describe '#gravatar_url_for' do it 'return an gravatar image url for given email' do email = 'test@example.com' size = 40 expect(helper.gravatar_for(email, 40)).to eq( "http://gravatar.com/avatar/#{Digest::MD5.hexdigest(email)}.png?s=40" ) end endend