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…

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

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

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.

bumping for new looks in the morning and new subject

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.

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