03 Mar, 2009

Published at 06:45PM

Tagged with hacks, programming, rails, rake, rspec, ruby, test-unit, testing, and tips

This post has 1 comment

Get the most out of Test::Unit

As mentioned before, I’ve been considering a complete switch back to Test::Unit. Well, all new projects since that post have test/unit tests, but I’ve yet to decide if it’s worth converting old projects. Probably not, but we’ll see.

Coming from Rspec, there were a few things I really missed:

  • block structure for writing tests
  • really easy to test helpers
  • pretty test output when running tests
  • ability to record tests in a specdoc format

Well, it turns out that it’s not that hard to get those niceties in test/unit.

Block structure for writing tests

It’s now built-in to Rails test support, so tests now look like this:

1
2
3
test "should have some behavior" do
  # test some behavior
end

And if you’re on a stand-alone Ruby project, here’s the implementation.

In addition, Rspec also provides that before do and after do syntax for pre/post operations. As you know, test/unit has the setup and teardown methods. But you can easily convert those to accept a block:

1
2
3
4
5
6
7
8
9
class Test::Unit::TestCase
  def self.setup(&block)
    define_method(:setup, &block)
  end

  def self.teardown(&block)
    define_method(:teardown, &block)
  end
end

Since, originally, all you’re doing is overriding the methods with your own definition, this just automatically works.

Really easy to test helpers

This could be blatantly obvious to those familiar with test/unit, but I don’t think this was available early on. Either way, there’s now built-in support for testing helpers in your Rails projects, you just have to bring it to life. Here’s how:

1
2
3
4
5
6
7
8
9
10
11
12
13
# test/test_helper.rb
require 'action_view/test_case'

# test/helpers/application_helper_test.rb
class ApplicationHelperTest < ActionView::TestCase
  test "should set page title with a block" do
    result = page_title do
      '<em>Heading</em>'
    end

    assert_equal '<h1><em>Heading</em></h1>', result
  end
end

As you can see, all you have to do is inherit from ActionView::TestCase. And to run these tests, here’s a rake task:

1
2
3
4
5
6
7
8
namespace :test do  
  Rake::TestTask.new(:helpers) do |t|
    t.libs << "test"
    t.pattern = 'test/helpers/**/*_test.rb'
    t.verbose = true
  end
  Rake::Task['test:helpers'].comment = 'Run the helper tests in test/helpers'
end

A simple $ rake test:helpers should do the trick.

Pretty test output and specdoc testdoc

I’m going to mention the last two bullets in one section. While this isn’t the most sophisticated solution, if you have the “colored” and “redgreen” gems installed, you can get a pretty decent test output using this 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
require 'colored'

def banner(title, pad = 85)
  puts "\n#{title} ".ljust(pad, "*").yellow
end

def stripe
  puts ("-" * 84 + "\n").yellow
end

namespace :test do  
  Rake::TestTask.new(:helpers) do |t|
    t.libs << "test"
    t.pattern = 'test/helpers/**/*_test.rb'
    t.verbose = true
  end
  Rake::Task['test:helpers'].comment = 'Run the helper tests in test/helpers'
end

namespace :tests do
  desc "Documents all tests in doc/TESTDOC"
  task :doc do
    File.open(Rails.root + '/doc/TESTDOC', 'w') do |file|
      Dir.glob('test/**/*_test.rb').each do |test|
        test =~ /.*\/([^\/].*)_test.rb$/
        file.puts "#{$1.gsub('_', ' ').capitalize}:" if $1
        File.read(test).map { |line| /test "(.*)" do$/.match line }.compact.each do |t|
          file.puts " - #{t[1]}"
        end
        file.puts
      end
    end
  end

  desc "Execute all application tests, plus TESTDOC"
  task :run do
    # document a list of all executed tests
    # (used to match up with requirements)
    Rake::Task['tests:doc'].invoke

    # unit tests
    banner "EXECUTING UNIT TESTS"
    Rake::Task['test:units'].invoke
    stripe

    # functional tests
    banner "EXECUTING FUNCTIONAL TESTS"
    Rake::Task['test:functionals'].invoke
    stripe

    # helper tests
    banner "EXECUTING HELPER TESTS"
    Rake::Task['test:helpers'].invoke
    stripe

    # integration tests
    banner "EXECUTING INTEGRATION TESTS"
    Rake::Task['test:integration'].invoke
    stripe

    # performance tests
    banner "EXECUTING APPLICATION BENCHMARKS"
    Rake::Task['test:benchmark'].invoke
    stripe
  end
end

Just put that in a file called “tests.rake” and stick it in your lib/tasks/ directory. Then type $ rake tests:run from your project root. If all goes well, you’ll see something like this (excuse the lack of tests, this is from a somewhat fresh project at work):

As an added bonus, the task also records all of the tests into a TESTDOC file within the doc/ directory of your Rails application. Again, it’s not like this file is super sophisticated, but it serves as a list for all of the tests that your application is accountable for. Sometimes useful for mapping requirements to tests, if you have to deal with that sort of thing :-)

I’m always seeking improvements for things like this, so let me know if you have any thoughts and/or additions. You can find the rake task in gist format, so feel free to fork it and make it better.

And just to be complete, Jeremy McAnally has written several testing libraries that fit very well with test/unit (namely, context, stump, and matchy), so keep those in mind, too.

Happy testing!

Comments

Chris Tuesday, 03 Mar, 2009 Posted at 07:57PM

I’ve grown to love the simplicity of test/unit lately. I’m also playing around with shoulda as a lighter-weight alternative to rspec.

Just a note about helper tests – the common practice is to just drop them into test/units/helpers – rake test:units will pick them up automatically. A custom rake task was my original approach, but the subdirectory way is nice since it’s automatic.

Do you have something to say about this post?
Retype the image to the right Spam Hint: Are You Human? Textile Formatting Tips

or

Ryan Heath | Site Management A Ruby on Rails production.

This site is a Formed Function. Formed Function LLC | @formedfunction | Get in Touch