24

I am struggling with testing the update method in Rails. I am using the standard built in testing framework (test::unit) and Rails 3.0.8. I have created a minimum application for testing this now, but I can't get it to work. Here is what I do:

I create a new blank rails application:

rails new testapp

Create a model called Collection:

rails generate model collection name:string

Run rake db:migrate:

rake db:migrate

Create a controller called Collections with an update method:

rails generate controller collections update

In collection_controller.rb I add this minimum update method:

def update
  @collection = Collection.find(params[:id])
  @collection.update_attributes(params[:collection])
end

The test fixtures are the defaults (collections.yml):

one:
  name: MyString

two:
  name: MyString

Then I add this to the collection_controller_test.rb under functional:

test "should update collection" do
  put :update, :id => collections(:one), :collection => {:name => 'MyString2'}
  assert_equal "MyString2", collections(:one).name
end

When I run the test:

rake test:functionals

It fails with this message:

test_should_update_collection(CollectionsControllerTest) [/Users/atle/Documents/Rails/testapp/test/functional/collections_controller_test.rb:6]:
<"MyString2"> expected but was
<"MyString">.

Here are the output from test.log:

[1m[36mSQL (0.2ms)[0m  [1m SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
[0m
[1m[35mCollection Load (0.1ms)[0m  SELECT "collections".* FROM "collections" WHERE "collections"."id" = 980190962 LIMIT 1
Processing by CollectionsController#update as HTML
Parameters: {"id"=>#<Collection id: 980190962, name: "MyString", created_at: "2011-06-12 13:14:13", updated_at: "2011-06-12 13:14:13">, "collection"=>{"name"=>"MyString2"}}
[1m[36mCollection Load (0.2ms)[0m  [1mSELECT "collections".* FROM "collections" WHERE "collections"."id" = 980190962 LIMIT 1[0m
[1m[35mAREL (0.2ms)[0m  UPDATE "collections" SET "name" = 'MyString2', "updated_at" = '2011-06-12 13:14:13.394135' WHERE "collections"."id" = 980190962
Rendered collections/update.html.erb within layouts/application (1.5ms)
Completed 200 OK in 9ms (Views: 4.5ms | ActiveRecord: 1.2ms)

In the log I can see the UPDATE statement, but I never see a new SELECT statement.

Could someone explain to me what I am doing wrong please?

insumity
  • 5,311
  • 8
  • 36
  • 64
Atle
  • 330
  • 1
  • 2
  • 7
  • 3
    +1 for a clearly asked question. Nice to see a new user asking an explicit question with this level of detail. Your problem is definitely stemming from `collections(:one)` in your assertion, which simply reads from the fixture again, instead of the db. Cameron and Teddy both have this correct below. (BTW: Look into `factory_girl` and dump fixtures entirely ASAP. They are convenient for getting started, but a horrible mess later on.) – jdl Jun 12 '11 at 14:20
  • So the fixtures and the db are two distinct locations. I assumed the fixtures are read into the db at startup and that collections(:one) read from the db. Thanks for clearing things and I'll look into that factory_girl :) – Atle Jun 12 '11 at 15:18

3 Answers3

17

You can make assertions on the instance variable that is created during the HTTP response:

test "should update collection" do
  put :update, :id => collections(:one), :collection => {:name => 'MyString2'}
  assert_equal "MyString2", assigns(:collection).name
end
Teddy
  • 18,357
  • 2
  • 30
  • 42
  • Your suggestion worked too, but I accepted Camerons answer, since he was first ;) Thank you! – Atle Jun 12 '11 at 15:07
  • This is the better answer, since it doesn't use the database. – schmijos Jul 16 '15 at 12:30
  • 1
    This is the correct answer. Collection.find(collections(:one)) is ugly. – hellion May 08 '16 at 19:14
  • 1
    Funny thing.. [this approach is deprecated](https://blog.bigbinary.com/2016/04/19/changes-to-test-controllers-in-rails-5.html#deprecation-of-assigns-and-assert_template-in-controller-tests) these days. – mlt Jul 17 '18 at 23:55
16

You're reloading the fixture data instead of reading from the db. Try something like

assert_equal "MyString2", Collection.find(collections(:one)).name

You might need to specify the id in the fixture.

Disclaimer: I haven't tested this.

bluehallu
  • 10,205
  • 9
  • 44
  • 61
Cameron Walsh
  • 786
  • 5
  • 9
  • 1
    Don't use the DB in the test if you don't need to. You don't want to test if the DB works, right? – schmijos Jul 16 '15 at 12:29
4

My favorite variant of this:

test "should update collection" do
  coll = collections(:one)
  put :update, :id => coll, :collection => {:name => "MyString2"}
  assert_equal "MyString2", coll.reload.name
end

The assigns(:collection) trick doesn't actually guarantee the record was saved. This may not be a problem, or it may be a surprise.

Nate
  • 4,561
  • 2
  • 34
  • 44