13. Stub all the things
describe "#show" do
subject { -> { get :show, id: id } }
let(:id) { '77' }
let(:pizza) { Pizza.new }
context "with an existing pizza" do
before { Pizza.should_receive(:find).with(id).and_return(pizza) }
it { assigns(:pizza).should == pizza }
end
context "with a non-existent pizza" do
before { Pizza.should_receive(:find).with(id).and_raise_error(ActiveRecord::RecordNotFound)
it { should raise_error(ActiveRecord::RecordNotFound) }
end
end
14. Everything is
integration
As a user
Given there is a pepperoni pizza
When I visit the pizza index page
And I click on "pepperoni"
Then I should see the pepperoni pizza
28. Imperative
“When deleting a user, if the
current user is an admin user,
then allow the deletion; if the
current user is not an admin,
do not allow the deletion to
finish.”
30. Imperative
“When a request for a
resource comes in, if the
request is for JSON, then fetch
the resource and render it
from the JSON template; if the
request is for HTML, then fetch
the resource and render the
HTML template; if the request
is for another format like PDF,
return an error.”
57. “Big” actions can be
simple…
# e.g. a show action
it { should assign(:some_resource) }
# e.g. a create action
it { should change(Pizza, :count).by(+1) }
58. Authentication
describe CommentsController do
let(:current_user) { users(:claude) }
let(:blog_post) { blog_posts(:top_ten_pizzas) }
describe "#new" do
subject { -> { get :new, blog_post_id: blog_post } }
context "with a logged in user" do
before { sign_in(:user, current_user) }
it "should not redirect to the login page" do
response.should_not be_redirect
end
end
context "with an unauthenticated user" do
it "should redirect to the login page" do
response.should be_redirect_to(sign_in_path)
end
end
end
end
59. Authentication shared
example
shared_examples_for "an action that requires a login" do
before { sign_out :user }
it { should respond_with_redirect_to(sign_in_path) }
end
60. Authentication shared
example in action
describe CommentsController do
let(:current_user) { users(:claude) }
let(:blog_post) { blog_posts(:top_ten_pizzas) }
before { sign_in(:user, current_user) }
describe "#new" do
subject { -> { get :new, blog_post_id: blog_post } }
it_should_behave_like "an action that requires a login"
end
end
61. Authorization
describe "#create" do
subject { -> { post :create, blog_post_id: blog_post, comment: params } }
let(:params) { { body: "What a great post. I loved the part about shared examples." } }
before { sign_in :user, current_user }
context "with an authorized user" do
let(:current_user) { users(:bob) }
it "should respond with created" do
response.should respond_with 201
end
end
context "with an unauthorized user" do
let(:current_user) { users(:mallory) }
it "should respond with 404" do
response.should respond_with 404
end
end
end
63. Authorization shared
example
shared_examples_for "an action that requires authorization" do
before { sign_in :user, users(:mallory) }
it { should respond_with 404 }
end
64. Authorization shared
example in action
describe "#create" do
subject { -> { post :create, blog_post_id: blog_post, comment: params } }
let(:params) { { body: "What a great post. I loved the part about shared examples." } }
before { sign_in :user, users(:bob) }
it_should_behave_like "a non-navigation action that requires a login"
it_should_behave_like "an action that requires authorization"
end
66. Presence shared
example
shared_examples_for "an action that requires" do |*resources|
resources.each do |resource|
context "with an invalid or missing #{resource}" do
let(resource) { double(to_param: "does-not-exist", reload: nil) }
it { should respond_with 404 }
end
end
end
67. Presence shared
example in action
describe PizzaController do
describe "#show" do
subject { -> { get :show, id: pizza, format: format } }
let(:pizza) { pizzas(:pepperoni) }
it_should_behave_like "an action that requires", :pizza
end
end
69. Response shared
example
shared_examples_for "an action that returns" do |*acceptable_formats|
acceptable_formats.each do |acceptable_format|
context "expecting a response in #{acceptable_format} format" do
let(:format) { acceptable_format }
it { should_not respond_with_status(:not_acceptable) }
end
end
(%i(html js json xml csv) - acceptable_formats.collect(&:to_sym)).each do |unacceptable_format|
context "expecting a response in #{unacceptable_format} format" do
let(:format) { unacceptable_format }
it { should respond_with_status(:not_acceptable) }
end
end
end
70. Response shared
example in action
describe CommentsController do
let(:current_user) { users(:claude) }
let(:blog_post) { blog_posts(:top_ten_pizzas) }
let(:format) { :html }
before { sign_in :user, current_user }
describe "#show" do
subject { -> { get :show, id: comment, format: format } }
let(:comment) { blog_post.comments.first }
it_should_behave_like "an action that returns", :html
end
describe "#create" do
subject { -> { post :create, blog_post_id: blog_post, comment: params, format: format } }
let(:params) { { body: "What a great post. I loved the part about shared examples." } }
it_should_behave_like "an action that returns", :html, :json
end
end