Autotest your rubygem

flyerhzm Posted by flyerhzm on January 18, 2011

Last blog post, I have introduced how to use bundler and rvm to build a rubygem, today I will say something about testing a rubygem. 

RSpec

RSpec is a Behaviour-Driven Development tool for Ruby programmers. It's an alternative to ruby unit test framework. If you are going to use rspec to test your rubygem, I strongly recommend you to use rspec2, which does a lot of improvement from rspec1. Check more info here: http://relishapp.com/rspec.

I don't want to teach you how to use the rspec, I just give you a short instruction.

  1. define spec_helper.rb
  2. write spec tests, one spec file corresponds to one lib file
  3. use rspec SPEC_FILES to test

It would be better to display the test result with color and nested format, just define .rspec file as follows

--format nested
--color

Autotest

It's fine that you type rspec command to test your spec files each time, but the better way is to make tests run automatically. It really saves your time.

autotest is also easy to use, to install it

gem install autotest

If you work on Mac OS X 10.5 or higher, the following two gems are also helpful

gem install autotest-fsevent autotest-growl

autotest-fsevent makes filesystem modification detection more efficient.

autotest-growl sends test results to growl notification.

then define the global autotest configuration in ~/.autotest

# Include plugins
require 'autotest/fsevent'
require 'autotest/growl'
 
# Skip some paths
Autotest.add_hook :initialize do |autotest|
  %w{.git .DS_Store ._* vendor}.each { |exception| autotest.add_exception(exception) }
  false
end

and integrate autotest with rspec2, add a file in your rubygem ./autotest/discover.rb

Autotest.add_discovery { "rspec2" }

After that, you can type autotest at the root directory of your rubygem, you will see all the spec files are running and get the test results. The test process is as follows

  1. You write some codes/specs that make the tests failure.
  2. Autotest runs that modified specs (or spec according to modified codes) and get failures, then you will see the growl notification who tells you your tests failed.
  3. You change your codes/specs to fix the failures.
  4. Autotest runs that modified specs again and get success. then you will see the growl notification who tells you your tests passed.
  5. Autotest runs all the specs that check if your modification affects other codes/specs, then you will see the growl notification who tells you if your all tests passed or failed. 

I'm really glad to play with autotest, but there is a problem if it takes a long time (more than several minutes) to run all the tests. As each time you fix the failures, autotest will run all the specs again for you, that means each time I fix failures, I have to wait a long time which is not I expect.

Watchr

Watchr is a modern continuous testing (flexible alternative to Autotest). The flexible means you can define the test strategy about when to test and test what files. The following is the .watchr example for rails_best_practices gem.

def growl
  title = "Watchr Test Results"
  image = $?.success? ? "~/.watchr/images/passed.png" : "~/.watchr/images/failed.png"
  message = $?.success? ? "success" : "failed"
  growlnotify = `which growlnotify`.chomp
  options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'"
  system %(#{growlnotify} #{options} &)
end

def run(cmd)
  puts cmd
  system(cmd)
end

def spec(file)
  if File.exists?(file)
    run("rspec #{file}")
    growl
  else
    puts("Spec: #{file} does not exist.")
  end
end

def run_all_specs
  run "rake spec"
  growl
end

def run_suite
  system "clear"
  run_all_specs
end

watch("spec/.*/*_spec\.rb") do |match|
  puts(match[0])
  spec(match[0])
end

watch("lib/(.*/.*)\.rb") do |match|
  puts(match[1])
  spec("spec/#{match[1]}_spec.rb")
end

# Ctrl-\
Signal.trap 'QUIT' do
  puts " --- Running all tests ---\n\n"
  run_suite
end

# Ctrl-C
Signal.trap 'INT' do
  if @interrupted then
    abort("\n")
  else
    puts "Interrupt a second time to quit"
    @interrupted = true
    Kernel.sleep 1.5
    # raise Interrupt, nil # let the run loop catch it
    run_suite
    @interrupted = false
  end
end

What the watchr configuration do are

  1. watch the codes in lib directory, if any file modifications occur, run the corresponding spec file.
  2. watch the codes in spec directory, if any file modifications occur, just run the spec file.
  3. after each spec running, send growl notification.
  4. type Ctrl + \ to run all tests.
  5. type Ctrl + C to stop test.

While I'm developing the rails_best_practices gem, I always run watchr .watchr at the terminal, so whenever I changed the codes/specs, I will got a notification from growl, it makes me more efficient to write robust rubygem. I hope it is also helpful to you.

Enjoy autotest for your rubygem!

comments powered by Disqus