Forum: Ruby on Rails Problems with tests

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Joshua M. (Guest)
on 2009-04-16 13:23
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
Joshua M. (Guest)
on 2009-04-16 13:46
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?
Gavin (Guest)
on 2009-04-16 15:05
(Received via mailing list)
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>
Joshua M. (Guest)
on 2009-04-16 17:08
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. :-)
Phlip (Guest)
on 2009-04-16 17:42
(Received via mailing list)
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
Joshua M. (Guest)
on 2009-04-16 19:11
Thanks for your useful hints, Philip.
Marnen L. (Guest)
on 2009-04-16 19:31
(Received via mailing list)
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!  :P

(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
Phlip (Guest)
on 2009-04-16 22:39
(Received via mailing list)
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
This topic is locked and can not be replied to.