开发者

Simple rspec question

I'm really struggling trying to learn rspec :( So I hope you can give me a little bit of help with a really simple create-action in the controller. I would like to use Rspec::mocks for this, as I think that is the way to do it? Instead of having to hit the database when testing.

I'm having a before_filter:

def find_project
   @project= Project.find_by_id(params[:project_id])
end

The create action looks like this:

def create
  @batch = Batch.new(params[:batch])
  @batch.project = @project

  if params[:tasks]
    params[:tasks][:task_ids].each do |task_id|
      @batch.tasks << Task.find(task_id)
    end
  end

  if @batch.save
    flash[:notice] = "Batch created successfully"
    redirect_to project_batch_url(@project, @batch)
  else
    render :new
  end
end

I'm really in doubt when it comes to @batch.project = @project how do I def开发者_如何学Pythonine @project? And also the whole params[:tasks][:task_ids].each part.. Ya.. pretty much the whole thing :(

Sorry for this newbie question - Hope you guys can help or atleast point me in the right direction :)

Thanks


The idea of a controller spec is to check whether the actions are setting instance variables, and redirecting/rendering as needed. To set up the spec, you would normally create an object or a mock, set attributes/stubs, and then call the action, passing a params hash if necessary.

So for example (air code):

describe MyController do
  before(:each) do
    @project = mock_model(Project)
    Project.stub(:find_by_id) {@project}
    @batch = mock_model(Batch)
    Batch.stub(:new) {@batch}
  end

  it "should redirect to project_batch_url on success" do
    @batch.stub(:save) {true)
    post :create, :batch => { :some_key => :some_value }, :tasks => { :task_ids => [1,2,3] }
    response.should redirect_to(project_batch_url(@project,@batch))
  end

  it "should render :new on failure" do
    @batch.stub(:save) {false)
    post :create, :batch => { :some_key => :some_value }, :tasks => { :task_ids => [1,2,3] }
    response.should render_template("new")
  end
end

You can find lots more information about this in the RSpec Rails docs.


Using BDD helps you define your interfaces. So if your controller wants the project to create a batch and add some task id's, then "write the code you wish you had." In practice for controllers, this means trying to push logic out of the controller and into your models. Testing models tends to be more intuitive and are definitely faster than testing controllers.

Here are some possible specs (untested) from the "mockist" point of view:

# controller spec  
describe BatchesController do
  def mock_project(stubs={})
    @mock_project ||= mock_model(Project, stubs)
  end

  def mock_batch(stubs={})
    @mock_batch ||= mock_model(Batch, stubs)
  end

  context "POST create"
    it "calls #create_batch_and_add_tasks on the project"
      mock_project.should_receive(:create_batch_and_add_tasks).with(
        :batch => { :name => 'FooBatch' },
        :task_ids => [1,2,3,4]
      )
      Project.stub(:find).and_return(mock_project)

      post :create, :batch => { :name => 'FooBatch' }, :tasks => { :task_ids => [1,2,3,4] }
      # consider changing your params to :batch => { :name => 'FooBatch', :task_ids => [1,2,3,4] }
    end

    it "redirects to the project_batch_url on success" do
      mock_project(:create_batch_and_add_tasks => mock_batch(:save => true))
      Project.stub(:find) { mock_project }
      post :create, :these_params => "don't matter because you've stubbed out the methods"
    end

# controller
def create
  @batch = @project.create_batch_and_add_tasks(
             :batch => params[:batch],
             :task_ids => params[:tasks].try([:tasks_ids])
           )
  if @batch.save
    ...
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜