Bob Silva posted Testing Gotchas in Rails in mid-October. I had fallen across this error myself a few times.

Well, today I had a huge code base I needed to check. I wrote the following Rake task:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Find missing fixtures declarations from Rails tests
# Code by Francois Beausoleil (francois@teksol.info)
# Released in the public domain.  Do as you wish.
desc "Finds missing fixtures declarations from your tests"
task :find_missing_fixtures do
  state = :find_test_case
  test_case = nil

  Dir['test/*/*_test.rb'].each do |file|
    File.open(file, 'r') do |f|
      f.each do |line|
        case state
        when :find_fixture
          case line
          when /def test_/
            printf "%s: %s\n", file, test_case
            state = :find_test_case
          when /fixtures/
            state = :find_test_case
          when /class (\w+) < Test::Unit::TestCase$/
            test_case = $1
            state = :find_test_case
          end

        when :find_test_case
          case line
          when /class (\w+) < Test::Unit::TestCase$/
            test_case = $1
            state = :find_fixture
          end
        end
      end
    end
  end
end
Run it like this:
1
2
3
4
5
$ rake find_missing_fixtures
(in D:/wwwroot/wpul.staging.teksol.info)
test/unit/name_test.rb: NameTest
test/unit/application_helper_test.rb: ApplicationHelperTest
test/unit/application_helper_test.rb: ApplicationHelperTruncationTest

Why do I report both the file and TestCase name ? Because I put more than one TestCase per test file, as I reported in Test fixtures and behavioral testing

Of course, if I could do away with fixtures altogether…

Yesterday, I was coding a new set of tests for a Picture class. I wanted to introduce an on-disk cache, when the picture is public.

I already had an existing test/unit/picture_test.rb, which I extended.

The tests looked like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class PictureTest < Test::Unit::TestCase
  def test_picture_exists_on_disk
    pict = uploaded_file('large.jpg', 'image/jpeg', 'somepicture.jpg')
    @picture = Picture.build(pict)
    @picture.public = true
    assert_model_saved @picture

    assert File.exist?(@picture.cache_filename), 'file copied to disk'
  end

  def test_thumbnail_exists_on_disk
    pict = uploaded_file('large.jpg', 'image/jpeg', 'somepicture.jpg')
    @picture = Picture.build(pict)
    @picture.public = true
    assert_model_saved @picture

    assert File.exist?(@picture.cache_thumbnail), 'file copied to disk'
  end
end

I knew this was the wrong way to go – the fixture was repeated more than once. Then, I realized that what I wanted was a new TestCase. I had already reached for my mouse to hit New File when I had a flash: what prevents me from simply adding a new class to picture_test.rb ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class PictureTest < Test::Unit::TestCase
  def test_read_from_stringio
    ...
  end
end

class PublicPictureTest < Test::Unit::TestCase
  def setup
    pict = uploaded_file('large.jpg', 'image/jpeg', 'somepicture.jpg')
    @picture = Picture.build(pict)
    @picture.public = true
    assert_model_saved @picture
  end

  def test_picture_exists_on_disk
    assert File.exist?(@picture.cache_filename), 'file copied to disk'
  end

  def test_thumbnail_exists_on_disk
    assert File.exist?(@picture.cache_thumbnail), 'thumbnail copied to disk'
  end
end

class PrivatePictureTest < Test::Unit::TestCase
  include PicturePathHelper

  def setup
    pict = uploaded_file('large.jpg', 'image/jpeg', 'somepicture.jpg')
    @picture = Picture.build(pict)
    @picture.public = false
    assert_model_saved @picture
  end

  def test_picture_not_copied_to_disk_when_private
    assert !File.exist?(@picture.cache_filename),
        'image should not be on disk when picture is private'
  end

  def test_thumbnail_not_copied_to_disk_when_private
    assert !File.exist?(@picture.cache_thumbnail),
        'thumbnail should not be on disk when picture is private'
  end
end

I also had another flash: is TestCase the right name ? Shouldn’t it be called TestFixture instead ? Because that is what instances of this class represent – a test fixture.

This is very much akin to Spec::Context, from RSpec.

And you see the tests ? They all respect the one assertion per test. I was quite happy with the results.

Now, I need to refactor those other instances in my test suite…

Named Test Fixtures

October 18th, 2005

In one of my projects, I have tons of units and functional tests. To speed things up, I set my configuration like this:

1
2
3
4
class Test::Unit::TestCase
  self.use_transactional_fixtures = true
  self.use_instantiated_fixtures  = :no_instances
end
In my tests, instead of accessing @bob, I now have to do:
1
2
3
4
5
def test_party_is_destoryed
  party = Party.find(@parties['bob']['id'])
  party.destroy
  assert party.destroyed?, "Party wasn't marked destroyed"
end
Works, but isn’t this very verbose ? So, I created myself a little helper::
def party(fixture_name)
  Party.find(@parties[fixture_name.to_s]['id'])
end
All great and dandy. I wanted to share with the community, so I started digging into fixtures.rb to implement it. Much to my surprise, this already exists:
1
2
3
4
5
6
7
8
class Test::Unit::TestCase #:nodoc:
  def self.fixtures(*table_names)
    table_names = table_names.flatten
    self.fixture_table_names |= table_names
    require_fixture_classes(table_names)
    setup_fixture_accessors(table_names)
  end
end

So, I guess I’m going to send a documentation patch instead of code !

UPDATE 1 (Oct 18, 2005): Well, would you look at that, this is all documented in the Class: Fixtures (See the last paragraph under Using Fixtures)

UPDATE 2 (Oct 25, 2005): Rails 0.14.1 is pre-configured like that. In fact, Mike Clark wrote Faster Testing with Rails 1.0 to explain the new behavior.

 

Search

A picture of me

I am François Beausoleil, a Ruby on Rails coder. During the day, I work on XLsuite. At night, I am interested many things. Read my biography

Tags

(3) (1) (0) (2) (1) (1) (2) (2) (1) (2) (1) (2) (1) (2) (1) (1) (1) (1) (2) (14) (1) (1) (1) (1) (2) (1) (1) (2) (0) (1) (2) (1) (3) (1) (1) (1) (1) (1) (1) (0) (3) (2) (1) (2) (2) (1) (3) (2) (8) (8) (9) (12) (1) (1) (3) (1) (1) (1) (1) (1) (1) (2) (2) (2) (1) (1) (3) (1) (3) (1) (0) (23) (1) (1) (0) (1) (1) (1) (23) (25) (1) (1) (13) (1) (1) (2) (3) (1) (1) (4) (1) (2) (3) (0) (1) (7) (3) (1) (5) (5) (2) (2) (2) (4) (6) (7) (1) (0) (1) (1) (2) (2) (1) (4) (12) (2) (1) (2) (4) (1) (1) (1) (2) (8) (2) (3) (2) (2) (1) (3) (1) (1)

Links

Projects I work on

Categories

Archives