why is my ActiveRecord model's state not saved during unit test?

I'm a rails noob, still working on my first test and seeing strange behavior - the state of the model that is changed during the method being tested is appearing to not be saved. I'm assuming I'm missing something basic here...

My Buzzuser model is listed below. In the migration that adds this table, I identify a column for level.

class Buzzuser < ActiveRecord::Base

def initialize     super     @level = 0   end

  def promote     @level += 1     puts "new level = ", @level

  end end

Here's the test:

class BuzzuserTest < ActiveSupport::TestCase   fixtures :ranks

  def test_promote     bu = Buzzuser.new     bu.promote     assert_equal(1, bu.level, "buzzuser level wasn't incremented")   end end

The output from the test, in the console, prints out (among other stuff):

new level = 1

But when it returns back to the test, the assertion fails. Why?

Thanks

ActiveRecord is cagey about how it stores the contents of db field attributes internally. You'd think messing w/a var called @db_field_name would affect the contents of my_instance.db_field_name, but it doesn't. I don't recall the specifics, but the short of it is--try getting rid of those @ symbols. That will make those instance var accesses into method calls, and those *should* work.

HTH,

-Roy

Thanks for your reply, Roy.

removing the @ symbols produced a different error:

NoMethodError: You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.+

The line identified was

level += 1

This is very confusing.

Hrm--sorry for the bum steer. This is working here:

  class Buzzuser < ActiveRecord::Base     def initialize       super       self.level = 0 unless self.level     end     def promote       self.level += 1
    end   end

I'm not positive why those 'self's are necessary...

[mailto:rubyonrails-talk@googlegroups.com] On Behalf Of Brian

That works. I think I tried that in a much earlier iteration of trying to get the tests to work. But I removed it because I thought 'self' means class-level. Is that only when self is used to define a method? And it's different when used on a variable?

I've got to admit, this is one of the kinds of things that makes me hesitant about RoR. It just doesn't make sense.

thanks for your help

Well, the meaning of 'self' changes depending on the execution context when the interpreter hits that line. There's a really nice discussion of this in _Ruby For Rails_ IIRC (I'm home today & my copy is at work, else I'd seem more intelligent ;-). When you tack it on to a method definition that lives inside a class, that makes the method a class method. But when you put it in an instance method, it's like c#'s 'this' reference (or vb's 'me').

Ah--and I think that reminds me why the self's are necessary--they help the interpreter realize that you don't mean to be working with a local variable--you want to be calling the instance method "level".

HTH,

-Roy

[mailto:rubyonrails-talk@googlegroups.com] On Behalf Of Brian

Pardee, Roy wrote:

ActiveRecord is cagey about how it stores the contents of db field attributes internally. You'd think messing w/a var called @db_field_name would affect the contents of my_instance.db_field_name, but it doesn't. I don't recall the specifics, but the short of it is--try getting rid of those @ symbols. That will make those instance var accesses into method calls, and those *should* work.

class Buzzuser < ActiveRecord::Base

Rename that to buzz_users and BuzzUser, plz!

def initialize     super     @level = 0

Not even. They must be like this:

   self.level = 0

That's because (as a Ruby thing, not just Rails) an x= creates a local x, even if a def x= were available in the next outer scope.

So how does testing work with Arrays of ActiveRecord relationships? The BuzzUser class has a

has_and_belongs_to_many :words

So what's the state of self.words in the unit test, and how is it initialized? In the test class, I'm testing against buzz_user.words.count and not getting expected results.

I've tried using the same as above, in the initializer I've added

self.words = Array.new

And the test is still failing. In the promote method, I'm finding certain words and adding them to the :words Array with

new_words = Word.find(...) self.words << new_words

I know the find method is finding results because I've assigned it to a local variable and output it with a puts statement. But the self.words contains nothing, even after the << statement. It's not throwing a nil exception either.

bumping for new looks in the morning and new subject

So how does testing work with Arrays of ActiveRecord relationships?

Ideally, you write the test first, and start using interface you want your objects to present. Then you write whatever joins and migrations are required to pass the test.

The BuzzUser class has a

has_and_belongs_to_many :words

So test:

   buzz = buzz_users(:some_fixture)    assert{ buzz.words == [ words(:shoe), words(:megaphone),                              words(:grunties) ] }

So what's the state of self.words in the unit test, and how is it initialized? In the test class, I'm testing against buzz_user.words.count and not getting expected results.

A habtm requires a table (without a model) named buzz_users_words. To test, write buzz_users_words.yml, and inside it write items like:

   some_fixture_shoe:      some_fixture: 1      shoe_id: 2 # id of shoe, etc

   some_fixture_shoe:      some_fixture: 1      megaphone_id: 3

   some_fixture_shoe:      some_fixture: 1      grunties_id: 4

Now fill out words.yml and buzz_users.yml with matching records with matching IDs.

I've tried using the same as above, in the initializer I've added

self.words = Array.new

Nope. You just told buzz_users to erase the links to the words, if any!

And the test is still failing. In the promote method, I'm finding certain words and adding them to the :words Array with

new_words = Word.find(...) self.words << new_words

Use << on objects and = on arrays - I think new_words is the latter.

One step at a time, dude! Do you have models and tests that cover a normal has_many, without the habtm? And what requirement do you have _right_now_ that asks for a habtm?

Ok I recant all the bad things I said about rails. :wink:

I missed something fundamental earlier with the fixtures and cases within fixtures. Until then, I had been using the fixtures only to load reference data, which the words table is - it's a static table. So I've got multiple buzzusers, each of which has a different subset of the words in the words table. That's why I chose habtm, because a user's words aren't simply a parent-child relationship. I already had the migration to create the buzzusers_words table.

once I started using the fixtures as test cases and how to load them in the tests, everything worked fine

thanks very much for your help Phlip