Mike Clark's - How Would You Test This
on September 18, 2007 @ 02:13 AM

Below is my rspec answer to Mike Clark’s recent post How Would You Test This which challenges people to post their own solution to how you’d test his MenuitemsController.

One thing Mike doesn’t mention about the frustration that people have with testing controllers is testing that testing controllers requires you to test your view associated with it (unless you are only redirecting).

There are ways around testing views, but most install a custom hack or installing a third party plugin or framework like rspec, Test::Rails or view_test.

Since Mike didn’t include any views in his original post I haven’t provided any tests for them in this post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
require File.dirname(__FILE__) + '/../spec_helper'

def do_post(attributes)
  post :create, :menu_item => attributes
end

describe MenuItemsController, 'Creating a new menu item (success)' do

  before do
    attributes = {'name' => "Enchilada", 'price' => 4.99}
    @menu_item = mock_model(MenuItem)
    MenuItem.should_receive(:new).with(attributes).once.and_return(@menu_item)
    @menu_item.should_receive(:save).with().once.and_return(true)

    do_post attributes
  end
  
  it 'redirects to index' do
    response.should redirect_to(menu_items_url)
  end
  
  it 'assigns @menu_item' do
    assigns[:menu_item].should be(@menu_item)
  end
  
  it 'sets the flash notice' do
    flash[:notice].should_not be(nil)    
  end
end

describe MenuItemsController, 'Creating a new menu item (failure)' do

  before do
    attributes = {'name' => "Enchilada", 'price' => 4.99}
    @menu_item = mock_model(MenuItem)
    MenuItem.should_receive(:new).with(attributes).once.and_return(@menu_item)
    @menu_item.should_receive(:save).with().once.and_return(false)

    do_post attributes
  end

  it 'renders new template on failed save' do
    response.should be_success
    response.should render_template('new')
  end
  
  it "assigns @menu_item" do
    assigns[:menu_item].should be(@menu_item)
  end

  it "not set the flash notice" do
    flash[:notice].should be(nil)      
  end
  
end

  1. Gregg Pollack 09.30.07 / 20PM

    I like how you put your do_post in your before do, that’s great when you’re just testing output.

    However, I’m a big believer in having 1 should/assert per spec/test. In your code you have on average 4 “shoulds” per test, since the ones in the setup are getting tested each time. If one of the shoulds failed in your setup, you’d get 3 failures.

    You’d have a harder time figuring out why the 3 failed, because none of the failures would tell you exactly where your code broke.

    if you created tests for each of the shoulds .. something like

    ‘Creating a new menu item should create a new Menu Item with the sent attributes’

    Then you’d know if MenuItem.new failed.

    Just my two bits.