Мне очень жаль, но я начинаю чувствовать, что бью себя по голове. RSpec меня совершенно сбивает с толку. Смотрел видео за видео, читал учебник за учебником, и все же я просто застрял на первом месте.
=== вот с чем я работаю
http://github.com/fudgestudios/bort/tree/master
=== Ошибки
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
=== вот действие моего контроллера
def bid
@bid = Bid.new(params[:bid])
@bid.save
end
=== вот мой тест
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
Очень обидно просыпаться на работу в 3 часа ночи и ничего не делать в течение дня. Пожалуйста пойми.





Обновлено: не следует ожидать, что Bid.count будет увеличиваться при использовании фиктивного объекта. Мантра, которую я забыл: кофеин перед кодом.
Пока просто комментирую строки, так что оригинал все еще там.
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
Постарайтесь написать как можно меньше спецификаций, напишите свои предложения таким образом, чтобы было очевидно, что вы должны проверять в этой спецификации. Например, как я поменял ваш с it "should work" на it "should create a new Bid". Если для этого контроллера есть что-то еще, напишите новую спецификацию
за каждую небольшую часть функциональности.
Если вам все-таки понадобятся имитирующие пользователей, есть несколько помощников для restful_authentication, которые упростят задачу. Сначала создайте пользовательский прибор в
RAILS_ROOT/spec/fixtures/users.yml, вот так:
quentin:
login: quentin
email: [email protected]
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"
Затем в своей спецификации вы сможете написать следующее и иметь свой метод current_user и все другие части restul_authentication
вести себя так, как вы ожидаете, во время выполнения.
login_as :quentin
# .... the rest of your spec
В качестве примера еще нескольких спецификаций я мог бы добавить еще пару примеров:
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
Пока я не совсем понимаю, что происходит. (с заглушками и лямбдой) ....
для
def bid
@bid = Bid.new params[:bid]
@bid.save
end
Следующие проходит !!
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
У вас есть пара вещей задом наперед (: each). Поскольку в примере указано, что сообщение должно увеличивать счетчик на 1, вы имеете дело с реальными записями, и нет никаких причин для того, чтобы что-либо заглушать. Кроме того, на данный момент, поскольку есть только один пример, нет причин для блока before. Я бы сделал так:
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
Одна вещь, которую я бы порекомендовал, - создавать эти вещи ОЧЕНЬ детально, пока вы не поймете их лучше. Начните с ожидания (сообщение должно изменить количество ставок), запустите спецификацию и позвольте сообщению об ошибке добавить все, что вам нужно, в спецификацию или в код.
Джесси,
Он все равно пройдет, если вы закомментируете 2-е две строки до (: each), которые не влияют на пример «следует создать новую ставку».
Ключевое слово lambda создает произвольный блок кода, который не выполняется, когда вы его определяете, но фактически является объектом, который вы можете назначить переменной и выполнить позже:
the_post = lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end
На данный момент этот код не выполняется, но мы можем ссылаться на него с помощью переменной the_post. Теперь мы можем отправить «следует», а затем «изменить ...», например:
the_post.should change(Bid, :count).by(1)
Когда эта строка выполняется, происходит несколько вещей. Сначала оценивается материал справа от 'should', инициализируя объект сопоставления rspec некоторыми инструкциями. Этот сопоставитель является аргументом «следует» - эквивалентом этого:
matcher = change(Bid, :count).by(1)
the_post.should(matcher)
Метод 'should' вызывается в the_post, который является блоком кода (который еще не был выполнен). Под капотом метод 'should' передает self (the_post) сопоставителю, поэтому теперь сопоставитель имеет все необходимое для оценки примера.
Сопоставитель вызывает Bid.count и записывает значение. Затем он выполняет блок (the_post), а затем повторно вызывает Bid.count и сравнивает его со значением, записанным ранее. В этом случае, поскольку мы ожидаем, что Bid.count изменится на 1 (здесь подразумевается положительное значение - увеличьте на 1), если это произойдет, сопоставитель молчит, и пример пройдет успешно.
Если значения совпадают или отличаются на какое-то значение, отличное от 1, пример завершится ошибкой. Вы можете увидеть эту работу, если измените ожидание на (2) вместо (1).
HTH, Дэйвид
Можете выложить код своей модели? Я предполагаю, что вы уже проверили, что он является производным от ActiveRecord :: Base.