RSpec脚手架控制器,了解给出的默认值

时间:2011-02-04 00:23:57

标签: rspec controllers scaffold

我正在通过一个rspec教程(peepcode教程)。我生成了一些脚手架,我希望有人可以帮助解释如何重新编写描述以便为新手阅读更清楚。

describe "POST create" do

    describe "with valid params" do
      it "assigns a newly created weather as @weather" do
        Weather.stub(:new).with({'these' => 'params'}) { mock_weather(:save => true) }
        post :create, :weather => {'these' => 'params'}
        assigns(:weather).should be(mock_weather)
      end

end

这行代码是我试图理解的这一行

Weather.stub(:new).with({'these' => 'params'}) { mock_weather(:save => true) }

我从未见过将方法放在括号内。这究竟意味着什么?

{ mock_weather(:save => true) }

2 个答案:

答案 0 :(得分:6)

该陈述意味着:

使用参数newWeather方法保存在类'these'=>'params'上,并返回表达式mock_weather(:save => true)的值

写一个类似的,也许更清晰的方法是:

Weather.stub(:new).with({'these'=>'params'}).and_return(mock_weather(:save => true))

语法{< some code> }创建code block,在调用存根时执行。

两种表单.and_return(){}的返回值略有不同;在第一种情况下,确定何时定义存根,在第二种情况下确定何时接收到消息。它们通常可以互换 - but sometimes not

修改

我觉得this answer误导了嘲笑并值得回应:

  

关于你的第一个问题   为了让它更清晰一点,“我们可以   从不使用模拟开始。一个模拟   当你不能时,对象很有用   取决于a的可预测行为   辅助对象,是一个对象   不重要但必须存在于   你的测试用例。典型的例子   模拟对象的用法是数据库   查询,网络使用,文件i / o。您   不希望你的测试失败,因为   你的电脑失去网络连接,   或者数据库不可用。

没错,模拟可以消除对外部资源的依赖,但这不是他们唯一的目的。模拟的真正价值在于它们允许您为尚不存在的代码编写测试。例如,在Rails中,您可能决定首先编写视图规范。

describe "posts/show.html.erb" do
  it "displays the author name" do
    assign(:post,mock('post',:author=>"Mark Twain"))
    render
    rendered.should contain("written by Mark Twain")
  end
end

此规范不需要存在数据库,控制器或模型。它所做的只是声明视图需要呈现一个字符串并验证它是否被渲染 - 这就是Rails视图应该关注的全部内容。唯一的依赖是模板文件和实例变量@post的存在,它由assign语句处理。它甚至不关心@post是什么,只是它响应:author

  

您生成的代码   导轨g脚手架不是最佳的。   生成的代码是在a中生成的   将使所有测试通过的方式,和   因为它使用模拟对象。我不   我知道为什么他们这样做了   更好的默认是失败的测试   所以你真的需要做   让它们通过的东西。

脚手架的整个想法是通过生成适用于通常用例的代码来节省时间。您不希望生成的测试实际上也能正常工作吗?

当然,通过使用脚手架,您正在围绕BDD / TDD“测试优先”范例进行最终运行,但可能您已经接受了所涉及的权衡,或者您首先不会使用脚手架。 / p>

对于“为什么使用模拟对象”,它们允许控制器规范与模型和数据库分离。所以,一旦你知道了推理,它就是“最优的”。

  

使用自动生成的模拟文件   根本不需要做任何事情   测试将继续通过   永远。这是一个坏主意,而且很糟糕   实践。

只要您不破坏主题代码,它们就会通过。因此,他们在回归测试中具有价值,以确保您不会以导致代码不再符合规范的方式引入新代码或重构。

  因为必须要写   模型文件中的验证规则,   而你没有使用模拟对象,   你可以肯定的是一个真实的   验证正在进行中。

这种耦合在Rails控制器规范中实际上是不合需要的。控制器应该尽可能少地了解模型,因此控制器规范只需要定义验证通过(或失败)时会发生什么 - 而脚手架提供的模拟就是这样做的。如果您需要测试模型实例是否对给定的参数集有效,请在模型规范中执行此操作。

答案 1 :(得分:1)

关于“如何让它更清晰”的第一个问题,我们可以从不使用模拟开始。

当您不能依赖辅助对象的可预测行为时,模拟对象很有用,辅助对象不是很重要但必须存在于您的测试用例中。模拟对象的典型使用示例是数据库查询,网络使用,文件i / o。您不希望测试失败,因为您的计算机丢失了网络连接,或者数据库不可用。

您从rails g scaffold生成的代码不是最佳的。生成的代码以一种使所有测试都通过的方式生成,为此它使用模拟对象。我不知道为什么他们这样做,我认为更好的默认是失败的测试,所以你实际上需要做一些事情让它们通过。

我会删除生成的模拟并执行以下操作:

#spec/controllers/weather_controller_spec.rb
describe "POST create" do
  describe "with valid params" do
    it "assigns a newly created weather as @weather" do
      post :create, :weather => {'location' => 'ORD', 'temp'=>'35', 'sample_time'=>'2011-02-04T20:00-0500'}
      assigns(:weather).should be_valid
    end

    it "should redirect you to the weather show page" do
      post :create, :weather => {'location' => 'ORD', 'temp'=>'35', 'sample_time'=>'2011-02-04T20:00-0500'}
      response.should redirect_to(weather_path(assigns[:weather]))
    end
  end

  describe "without valid params" do
    it "should notify that a location is required" do
      post :create, :weather => {'temp'=>'35', 'sample_time'=>'2011-02-04T20:00-0500'}
      flash[:notice].should == 'Location is required.'
      assigns(:weather).should_not be_valid
    end

    it "should notify that a temperature is required" do
      post :create, :weather => {'location' => 'ORD', 'sample_time'=>'2011-02-04T20:00-0500'}
      flash[:notice].should == 'A temperature is required.'
      assigns(:weather).should_not be_valid
    end

    it "should notify that a sample time is required" do
      post :create, :weather => {'location' => 'ORD', 'temp'=>'35'}
      flash[:notice].should == 'A sample time is required.'
      assigns(:weather).should_not be_valid
    end
  end
end

请注意,我们没有使用模拟对象,因此代码减少为使用一些参数进行POST调用并验证该对象是否有效。由于必须在模型文件中编写验证规则,并且您没有使用模拟对象,因此可以确保进行实际验证。

使用自动生成的模拟文件,您根本不需要做任何事情,测试将继续永远通过。这是一个坏主意,也是一种不好的做法。

另请注意,您应编写更多测试来执行参数无效或缺失的情况。再次,通过assigns(:weather).should_not be_valid,您正在验证您的验证正在完成其工作。

每次调用post :create时编写参数字典都是重复,脆弱和丑陋的。你应该学习如何使用灯具。例如,使用 Factory Girl

#spec/factories.rb
Factory.define :weather_valid do |f|
  f.location "ORD"
  f.temp "35"
  f.sample_time "2011-02-04T20:00-0500"
end


#spec/controllers/weather_controller_spec.rb
describe "POST create" do
  describe "with valid params" do
    it "assigns a newly created weather as @weather" do
      post :create, :weather => Factory.build(:weather_valid).attributes
      assigns(:weather).should be_valid
    end

    it "should redirect you to the weather show page" do
      post :create, :weather => Factory.build(:weather_valid).attributes
      response.should redirect_to(weather_path(assigns[:weather]))
    end
  end
...

这为您提供了可重用且更易读的代码。