Estupefato com RSpec
-
03-07-2019 - |
Pergunta
Sinto muito, mas isso está começando a sentir como me chutando na cabeça. Estou completamente perplexo com RSpec. Tem vídeos assistidos após vídeo, tutorial de leitura depois de tutorial, e ainda estou apenas preso em um quadrado.
=== aqui é o que eu estou trabalhando com
http://github.com/fudgestudios/bort/tree/master
=== erros
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
=== aqui é a minha ação do controlador
def bid
@bid = Bid.new(params[:bid])
@bid.save
end
=== aqui é o meu teste
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 / árvore / master / spec / spec_helper.rb
É muito desanimador para acordar para o trabalho em 3:00 e conseguir nada para o dia. Por favor, entenda.
Solução
Você tem um par de coisas para trás antes (: cada). Vendo como o exemplo está especificando que o cargo deve aumentar a contagem por 1, você está lidando com registros reais e não há nenhuma razão para arrancar alguma coisa. Além disso, neste momento, uma vez que existe apenas um exemplo, não há nenhuma razão para ter um bloco antes. Eu faria isso desta maneira:
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
Uma coisa que eu recomendo é a criação destas coisas muito granular por enquanto até que você compreendê-los melhor. Iniciar com a expectativa (pós deve mudar contagem bid), executar a especificação e deixe o guia mensagem de falha que você adicione qualquer outra coisa que você precisa na especificação ou no código.
Outras dicas
Jesse,
Ele ainda vai passar se você comentar as 2 duas linhas de antes (: cada)., Que estão tendo nenhum impacto sobre o "deve criar um novo lance" exemplo
A palavra-chave lambda cria um bloco arbitrário de código que não é executado quando você defini-lo, mas na verdade é um objeto que você pode atribuir a uma variável e executar depois:
the_post = lambda do
post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end
Neste ponto que o código não é executado, mas podemos nos referir a ele com o 'the_post' variável. Agora nós podemos enviá-lo 'deveria', seguido de 'Alterar ...', assim:
the_post.should change(Bid, :count).by(1)
Quando esta linha é executada, algumas coisas acontecem. O material para a direita do 'deve' é avaliado primeiro, inicializar um objeto de correspondência rspec com algumas instruções. Essa correspondência é o argumento de 'deveria' - o equivalente a isso:
matcher = change(Bid, :count).by(1)
the_post.should(matcher)
O método 'deve' é chamado em the_post, que é o bloco de código (que ainda não foi executada). Sob o capô, o método 'deve' passa self (the_post) para a correspondência, assim que a correspondência agora tem tudo que precisa para avaliar o exemplo.
A correspondência chama Bid.count e registros o valor. Em seguida, ele executa o bloco (the_post), e em seguida, chama Bid.count uma segunda vez e compara com o valor que anotou anteriormente. Neste caso, uma vez que estamos procurando Bid.count à mudança por 1 (positivo está implícito aqui - aumento de 1), se é isso que acontece as estadias matcher silenciosa e o exemplo passa
.Se os valores são os mesmos, ou diferem por algum valor diferente de 1, o exemplo falhará. Você pode ver que o trabalho se você mudar a expectativa de por (2) em vez de (1).
HTH, David
EDIT: você não deve esperar Bid.count de incremento ao usar um objeto fictício. Mantra eu esqueci: cafeína antes do código
.Apenas comentar as linhas, por agora, de modo que o original ainda está lá.
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
Tente escrever como especificações pequenas quanto possível, escrever seus setences de tal forma a torná-lo óbvio que você deve verificar em que spec. Por exemplo, como eu mudei o seu a partir it "should work"
para it "should create a new Bid"
. Se há mais do que o controlador, escrever uma nova especificação
para cada pequeno pedaço de funcionalidade.
Se você acabar precisando de usuários simulados, existem alguns ajudantes para restful_authentication que tornam mais fácil. Primeiro crie um dispositivo elétrico do usuário em
RAILS_ROOT/spec/fixtures/users.yml
, como este:
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"
Então, em sua especificação você vai ser capaz de escrever o seguinte e ter o seu método current_user
e todas as outras partes do restul_authentication
se comportam como você esperaria que em tempo de execução.
login_as :quentin
# .... the rest of your spec
Como um exemplo de mais algumas especificações que eu poderia acrescentar como mais um par de exemplos:
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
Enquanto eu não entendo muito bem o que está acontecendo. (Com tocos e lambda) ....
para
def bid
@bid = Bid.new params[:bid]
@bid.save
end
A seguir passa !!
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