Dazed RSpec
Sorry, but this is starting to feel like a kick to the head. RSpec. Watched video after video, read tutorial after tutorial and yet I just got stuck on the square.
=== this is what I am working with
http://github.com/fudgestudios/bort/tree/master
=== Errors
F
1)
NoMethodError in 'bidding on an item should work'
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.new_record?
spec/controllers/auction_controller_spec.rb:16:
spec/controllers/auction_controller_spec.rb:6:
Finished in 0.067139 seconds
1 example, 1 failure
=== here is my controller action
def bid
@bid = Bid.new(params[:bid])
@bid.save
end
=== here is my test
require File.dirname(__FILE__) + '/../spec_helper'
include ApplicationHelper
include UsersHelper
include AuthenticatedTestHelper
describe "bidding on an item" do
controller_name :items
before(:each) do
@user = mock_user
stub!(:current_user).and_return(@user)
end
it "should work" do
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
assigns[:bid].should be_new_record
end
end
=== spec_helper
http://github.com/fudgestudios/bort/tree/master/spec/spec_helper.rb
It's a shame that you have to wake up to work at 3 am and do nothing during the day. Please understand.
source to share
You have a couple of things back before (: each). As an example, it is indicated that the message should increase the score by 1, you are dealing with real records, and there is no reason to cut something down. Also, for now, since there is only one example, there is no reason to have before the block. I would do it like this:
describe ItemsController, "bidding on an item" do
fixtures :users
it "should create a new Bid" do
login_as :quentin
lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end.should change(Bid, :count).by(1)
end
end
One thing I would recommend is to create these things in VERY detail until you understand them better. Start by waiting (the message should change the number of bids), run the spec and let the failure message give you the option to add whatever you need in the spec or in code.
Jesse,
This will continue if you comment out the 2nd two lines before (: each), which does not affect the "must create a new ticket" example.
The lambda keyword creates an arbitrary block of code that is not executed when it is defined, but is actually an object that you can assign to a variable and execute later:
the_post = lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end
At this point the code is not executed, but we can refer to it using the "the_post" variable. Now we can send it "should" and then "change ...", for example:
the_post.should change(Bid, :count).by(1)
Several things happen when this line is executed. The stuff to the right of the "should" is first evaluated by initializing an rspec mate object with some instructions. This match is the "must" argument - the equivalent of this:
matcher = change(Bid, :count).by(1) the_post.should(matcher)
The 'should' method is called on the_post, which is a block of code (which hasn't been executed yet). Under the hood, the "must" method passes self (the_post) to the matches, so the helper now has everything it needs to evaluate the example.
The connector calls Bid.count and writes the value. It then executes the block (the_post) and then calls Bid.count a second time and compares it to the value it wrote earlier. In this case, since we want the Bid.count to change by 1 (positive here is implied - will increase by 1), if that happens, the matches are silent and the example passes.
If the values ββare the same or differ by some value other than 1, the example will fail. You can see this work if you change the expectation to (2) rather than (1).
NTN, David
EDIT: you shouldn't expect the Bid.count to increase when using the layout object. Mantra I forgot: caffeine before the code.
Just by commenting out the lines while the original is still there.
require File.dirname(__FILE__) + '/../spec_helper'
include ApplicationHelper
include UsersHelper
include AuthenticatedTestHelper
describe "POST to bid_controller" do
controller_name :items
before(:each) do
#@bid = mock_model(Bid) # create a new mock model so we can verify the appropriate things
#Bid.stub!(:new).and_return(@bid) # stub the new class method on Bid to return our mock rather than a new ActiveRecord object.
# this separates our controller spec entirely from the database.
end
it "should create a new Bid" do
lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end.should change(Bid, :count).by(1)
end
# ... more specs
end
Try to write as little specifications as possible, write your settings in such a way that it is obvious what you should check in that specification. For example, how I changed your from it "should work"
to it "should create a new Bid"
. If there is more for that controller, write a new specification for each small piece of functionality.
If you end up in need of mock users, there are some restful_authentication helpers that make things easier. First create a custom fixture in
RAILS_ROOT/spec/fixtures/users.yml
, for example:
quentin:
login: quentin
email: quentin@example.com
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
created_at: <%= 5.days.ago.to_s :db %>
activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9b
activated_at: <%= 5.days.ago.to_s :db %>
name: "Quentin"
Then in your spec, you can write the following and have your method current_user
and all other restul_authentication parts behave as you would expect them at runtime.
login_as :quentin
# .... the rest of your spec
As an example of a few additional specifications, I could add a couple more examples:
def do_post
# extracting the method under test, so I don't repeat myself
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end
it "should create a new Bid" do
lambda do
do_post
end.should change(Bid, :count).by(1)
end
it "should assign the Bid to the proper auction" do
@bid.should_receive(:auction_id=).with(1) # test this, I believe doing Bid.new(params[:bid]) sets the id directly not sets the model
do_post
end
it "should assign the Bid the proper points" do
@bid.should_receive(:point=).with(1)
do_post
end
source to share
I don't quite understand what is going on yet. (with cigarette butts and lambda) ....
for
def bid
@bid = Bid.new params[:bid]
@bid.save
end
The next ones are coming through !!
require File.dirname(__FILE__) + '/../spec_helper'
include ApplicationHelper
include UsersHelper
include AuthenticatedTestHelper
describe "bidding on an item" do
controller_name :items
fixtures :users
before(:each) do
@user = login_as :quentin
@bid = mock_model(Bid) # create a new mock model so we can verify the appropriate things
@bid.stub!(:new).and_return(@bid) # stub the new class method on Bid to return our mock rather than a new ActiveRecord object.
#Bid.stub!(:save).and_return(true)# this separates our controller spec entirely from the database.
end
it "should create a new Bid" do
lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end.should change(Bid, :count).by(1)
end
end
source to share