Saturday, November 24, 2007

It occurred to me that, even though I can't seem to get rspec installed, I still can move forward and write my first story; I'll try to use Test::Unit.

Well, I'll start with a controller.

ruby script/generate model Item

Looks good. The card says that I need to have a link to the picture and a description, so I should have a test that causes me to have those columns. Let's go over to the unit tests and edit item_test.rb. I'll replace the truth test with one of my own.

def test_has_a_picture_link_and_a_description
  expected_picture_link = "http://IAmALink/"
  expected_description = "WTF?"
  t = Item.create(
        :picture_link => expected_picture_link,
        :description => expected_description
        )
  assert_equal expected_picture_link, t.picture_link
  assert_equal expected_description, t.description
end

I run the test and get that it can't create an item. Let's go do a db:migrate to get the test table created.

After the db:migrate, I get the expected exception about undefined method description. Let's create a new migration and get it to add the columns.

(sidenote: I got autotest working on my machine, but it does an interesting problem that it doesn't apply the migrations to the test database like rake test:units does. I guess, until I figure that out, I'll just make sure that I run a rake test:units if I create a migration. I also installed snarl for my pc, but autotest isn't bringing them up. I guess that is a troubleshooting step for later).

After the migration, the test is passing.

Now that I've added the attributes, I can write a test for the controller that says I should be able to create a new one. I'll go generate a Controller and write a test for showing new:

def test_should_show_new
  get :new
  assert_response :success
  assert_template 'new'
  assert_not_nil assigns(:item)   
end

This test will cause me to need a new page. I'm going to see if a temporary scaffolding will help. I'll add the following to the top of the ItemController class.

scaffold :Item

Interesting, when I did a temporary scaffolding, the test came back that it was expecting "new" but rendering with "new.rhtml". I guess I won't use scaffolding, I'll just create a new action on the ItemController.

class ItemController < ApplicationController
  def new
  end
end

Interesting! The test failed because it couldn't find a view. I wasn't totally expecting to not be able to test the ItemController without a view. No problem, lesson learned, let's go make a new.rhtml with something stupid in just to have it. We'll start adding to it when we create tests for the view.

After adding the view, I get a failure on my test, as expected, since the new action doesn't create a new story. I'll add code to do it.

def new
  @item = Item.new
end

That did it! Awesome, my first passing controller test. This took me a lot longer than I had hoped, due to some obnoxious oversights on my part. We are back on track, though.

Now, let's write a test that posts to the new page and gets saved.

def test_should_add_new_item
    post :new, :item => {
        :description => 'The Description',
        :picture_link => 'http://link',
      }
    assert ! assigns(:item).new_record?
end

That fails, so I change new to have support for posting.

def new
  @item = Item.new(params[:item])
  if request.post?
    @item.save
  end
end

So far, so good. I'm going to add to the test to make sure that the attributes are set:

def test_should_add_new_item
  expected_description ='The Description'
  expected_picture_link = 'http://link'
  post :new, :item => {
      :description => expected_description,
      :picture_link => expected_picture_link,
    }
  assert ! assigns(:item).new_record?
  item = Item.find(:first)
  assert_equal expected_description, item.description
  assert_equal expected_picture_link, item.picture_link
end

Where am I missing something? I would have expected this to pass, but it is telling me that item.description is nil. That seems strange.Now, if I have the following lines, then it passes. I'm misunderstanding something about the test facilities that it doesn't have an item in the database to get with item.find(:first).

item = assigns(:item)
assert ! item.new_record?
assert_equal expected_description, item.description
assert_equal expected_picture_link, item.picture_link

Oh well, I'll stick with that for now. I guess the new_record? will make sure that it got put in the database.

So, where am I? I have a new action on my ItemController that correctly creates a new Item with a get and stores the new item with a post. Excellent.

Now, I've got about half an hour left, so I'm going to try to write some tests to force a form to be displayed on the view.

Let's see if this works:

def test_should_show_new_form
  get :new
  assert_response :success
  assert_select 'input', :count=>1
end

I get an assertion failure that it expected at least 1, found 0. Let's see if adding that to the view helps:

<% form_for :item do |f| %>
  <p><%= f.text_area :picture_link %></p>
<% end %>

Well, that worked. I had a couple false starts, because I had done an assert_select 'select' rather than 'input', so I struggled a bit. I need to pay more attention. I think I'm copying too much from examples and not thinking it through. Let's add a little more to the test to experiment a bit with assert_select.

assert_select 'input[id=item_picture_link]', :count=>1

Looks good. Let's add one for the description, although this is going to be a text area.

assert_select 'textarea[id=item_description]', :count=>1

Jumping back to the view, we can add this line:

<p><%= f.text_area :description %></p>

to get this test to pass. Now, let's add the submit button.

assert_select 'input[type=submit]', :count=>1

I get this test passing with the following line:

<p><%= submit_tag %></p>

And, presto! I now have a form. I'm going to open it up in a browser, look at it, then submit a new item. I'll load up WEBrick and then go to http://localhost:3000/item/new to view my gorgeous page. AWESOME!

Well, after putting some stuff in and submitting it, I jump over to the ruby console and put in the following:

>>Item.count
=> 1
>>Item.find(:first)
=> #<Item:0x4c70084 @attributes={"id"=>"2", "description"=>"This is a sample what is it", "picture_link"=>"http://sample_link"}>
>>

Looks like exactly what I wanted!

Summary

I had a lot of problems due to lack of attention on my part (I was using the Rails book that I just went through as an example, so I ended up with a lot of :story instead of :item) and also because I am just learning the testing framework/rails. I should have been able to get the new action done from start to finish in much sooner, but I am happy about all that I learned along the way. I hope the next action will be faster. :) Or, at the very least, that I learn as much.

All in all, though, I'm starting to feel a lot better in the environment, and I think I'm going to enjoy this a lot. I love being able to test-drive my entire view without actually having to pull up a browser. I'm going to have to start worrying about the styling eventually, but not yet.

Autotest is very useful. I couldn't get it working on windows earlier, but I found this tutorial on getting autotest working in windows. I haven't seen the Snarl stuff work, but I'm going to look at that later.

Unfortunately, I'm out of time for today, so I'm going to wrap this up and put it off until later.

Saturday, November 24, 2007 4:30:27 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]