0

对不起,但这开始感觉像是在踢自己的头。我对 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 点起床上班,一整天一事无成,真是令人沮丧。敬请谅解。

4

4 回答 4

18

在 before(: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

我推荐的一件事是现在非常精细地创建这些东西,直到你更好地理解它们。从期望开始(帖子应该更改出价计数),运行规范并让失败消息指导您在规范或代码中添加您需要的任何其他内容。

于 2009-01-01T17:46:13.467 回答
3

杰西,

如果您注释掉 before(:each) 的第二两行,它仍然会通过,这对“应该创建一个新的 Bid”示例没有影响。

lambda 关键字创建一个任意代码块,在定义它时不会执行,但实际上是一个可以分配给变量并稍后执行的对象:

the_post = lambda do
  post 'bid', :bid => { :auction_id => 1, :user_id => @user.id, :point => 1 }
end

此时代码没有被执行,但我们可以使用 'the_post' 变量来引用它。现在我们可以发送“should”,然后发送“change ...”,如下所示:

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 以外的某个值,则示例将失败。如果将期望更改为 by(2) 而不是 by(1),您可以看到该工作。

HTH,大卫

于 2009-02-14T12:20:07.450 回答
2

编辑:您不应该期望 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: 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"

然后在您的规范中,您将能够编写以下内容并让您的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
于 2009-01-01T16:12:13.360 回答
0

虽然我不太明白发生了什么。(带有存根和 lambda)....

为了

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
于 2009-01-01T17:13:09.713 回答