Problems with tests


#1

Hi all

I’m trying to write good unit tests. Here’s part of my blog_test.rb:

— blog_test.rb:

require File.dirname(FILE) + ‘/…/test_helper’

class BlogTest < Test::Unit::TestCase
fixtures :blogs,
:comments

def setup
@valid_blog = Blog.new(valid_blog_attributes)
end

def test_should_be_able_to_have_many_comments
@valid_blog.comments = {}
assert_valid @valid_blog

comments.each do |comment|
  @valid_blog.comments << comment
  assert_not_nil @valid_blog.comments
  assert_valid @valid_blog
end

end
end

— comments.yml:

first_comment:
id: 1
subject: “Dies ist der erste Kommentar”
body: “Dies ist der Inhalt des ersten Kommentars”

second_comment:
id: 2
subject: “Dies ist der zweite Kommentar”
body: “Dies ist der Inhalt des zweiten Kommentars”

The Comment model is :polymorphic => true, :as => :commentable.

As we can see there are two comments available in the fixtures, but in
when running the test_should_be_able_to_have_many_comments test, there
are none!

puts comments.size

returns 0 all the time!
Why aren’t they available?

Thanks a lot for help.
Josh


#2

It seems that the fixtures are not correct (not complete). When I
specify the commentable_type and _id, it works:

commentable_type: “Blog”
commentable_id: 1

Why is that? I’d like to put the fixtures as loose as possible into the
database and then hardwire them to the needed other objects in each
test, so for the blog_test.rb I wanted to do:

def setup
@valid_blog = Blog.new(valid_blog_attributes)
@valid_blog.comments << comments
end

…and I hoped that this would automatically set the commentable_type
and _id to the needed values then.

But this doesn’t seem so. So I guess I have to hardwire the stuff all in
the fixtures… Or are there other, better ways to achieve better
dynamics?


#3

Why not test by creating comments - bypass the comments fixtures all
together?

def test_should_be_able_to_have_many_comments
blog = blog.create!(valid_blog_attributes)
assert_difference “blog.comments” do
blog.comments.create!(valid_comment_attributes)
end
end

what version of rails are you using?

On Apr 16, 10:46 am, Joshua M. removed_email_address@domain.invalid


#4

Gavin wrote:

Why not test by creating comments - bypass the comments fixtures all
together?

def test_should_be_able_to_have_many_comments
blog = blog.create!(valid_blog_attributes)
assert_difference “blog.comments” do
blog.comments.create!(valid_comment_attributes)
end
end

what version of rails are you using?

On Apr 16, 10:46�am, Joshua M. removed_email_address@domain.invalid

Thanks, but I’m doing it now the hardwired way. :slight_smile:


#5

Joshua M. wrote:

def setup
@valid_blog = Blog.new(valid_blog_attributes)

Don’t construct a blog - fetch one from the fixtures:

   @valid_blog = blogs(:valid_blog)

end

def test_should_be_able_to_have_many_comments
@valid_blog.comments = {}
assert_valid @valid_blog

The assertion that the blog is valid should be in the setup or another
test.
Each test case should only speak to one activity.

comments.each do |comment|

Where does comments come from? you are probably picking up comments(),
which is
the fixture loader. It returns an array unless you pass one comment, so
you are
now slipping off that array. Try this:

   comments(:first_comment, :second_comment).each do |comment|
  @valid_blog.comments << comment
  assert_not_nil @valid_blog.comments

As you get better at testing, you will get bored with testing that
ActiveRecord
behaves as advertised, and you will test your actual logic.

To set a good example here, don’t test against nil, because if comments
were an
empty array, then assert_not_nil [] would pass.

Instead, test that the comment went in:

     assert{ @valid_blog.comments.last == comment }

That illustrates two things. It shows my assert{ 2.0 }, which turns any
expression into a generic assertion, and it shows AR evaluates == as
true if two
records have the same ID.

     assert{ @valid_blog.valid? }
end

end
end

— comments.yml:

first_comment:
id: 1
subject: “Dies ist der erste Kommentar”
body: “Dies ist der Inhalt des ersten Kommentars”

If you use Rails >2, don’t use the id: 1. Let the fixturizer build the
number
for you. This makes coding much simpler.

Next, don’t call it first_comment. Name it after the theme of your
comment.


Phlip


#6

Thanks for your useful hints, Philip.


#7

On Apr 16, 9:37 am, Phlip removed_email_address@domain.invalid wrote:

Joshua M. wrote:
[…]

def setup
@valid_blog = Blog.new(valid_blog_attributes)

Don’t construct a blog - fetch one from the fixtures:

   @valid_blog = blogs(:valid_blog)

Why? In my experience, fixtures make tests brittle and hard to
understand, so I’ve stopped using them entirely. What’s the advantage
here?

[…]

As you get better at testing, you will get bored with testing that ActiveRecord
behaves as advertised, and you will test your actual logic.

Yeah. ActiveRecord has already been well tested. Focus on the code
you wrote.

(I know, that’s a tough concept to internalize – I certainly don’t
always manage…)

To set a good example here, don’t test against nil, because if comments were an
empty array, then assert_not_nil [] would pass.

Instead, test that the comment went in:

     assert{ @valid_blog.comments.last == comment }

That illustrates two things. It shows my assert{ 2.0 }, which turns any
expression into a generic assertion,
[…]

Cool! Sexy RSpecish syntax for the unsexy Test::Unit! I’ll have to
try it out! :stuck_out_tongue:

(Seriously, I think assert2 looks great, and I’m looking forward to
trying it.)

Best,

Marnen Laibow-Koser
http://www.marnen.org
removed_email_address@domain.invalid


#8

Marnen Laibow-Koser wrote:

def setup
@valid_blog = Blog.new(valid_blog_attributes)
Don’t construct a blog - fetch one from the fixtures:

   @valid_blog = blogs(:valid_blog)

Why? In my experience, fixtures make tests brittle and hard to
understand, so I’ve stopped using them entirely. What’s the advantage
here?

(Let’s hope you have not replaced them all with totally empty mocks!)

The advice for newbs, and possibly also for the original poster, is to
use
fixtures the way the tutorials specify. This gets them out of the
starting gate.

The advice, as a project grows, is to use some form of “scenarios”,
which are
kits of fixtures tuned to specific situations. Example: new_customer,
paid_up_customer, delinquent_customer, etc.

If you don’t use raw fixtures, you should find yourself implementing a
scenario
system directly into your setup blocks. From there, you should re-use
their
support methods into some scenario library.

Finally, I suspect the most advanced advice possible is to always write
assertions that gracefully adapt to your data records, whether they be
classic
range-free fixtures, or isolated scenarios. Don’t do this:

records = get_fithy_records()
assert{ records.length == 17 }

That breaks when you add a new filthy record, for whatever reason!
Instead, do this:

assert{ records.map(:status).uniq == [‘filthy’] }

This link emblogs the ideal of writing flexible tests, even when your
code
happens to implement something complex, such as a Minimum Spanning Tree:

http://broadcast.oreilly.com/2009/02/merb-mind-maps.html

As you get better at testing, you will get bored with testing that ActiveRecord
behaves as advertised, and you will test your actual logic.

Yeah. ActiveRecord has already been well tested. Focus on the code
you wrote.

(I know, that’s a tough concept to internalize – I certainly don’t
always manage…)

When you are getting started with a new library, writing unit tests
directly on
the library is a best practice. So, you should indeed precede
“Foo.has_many
:bars” with a test that a foo object has a .bars collection full of
bars.


Phlip
http://flea.sourceforge.net/resume.html