Autotest is a great time-saver when doing BDD (or TDD for the less-enlightened, lol). Autospec seemlessly integrates Rspec. However, if you don’t use the expected “spec/*_spec.rb” structure, autotest will not find your specs – or examples, as I call them.
It is very easy to use whatever structure you want using the following steps:
Telling Autotest to use Rspec as the test runner
The first thing you want to happen is for Autotest to use Rspec as the test runner. This usually happens automatically using autospec, which tests for the presence of the spec directory. Since we do not want a spec directory, we must tell explicitly tell Autotest to do this. So:
Step 1: in your project directory create a file autotest/discover.rb with the following line:
Autotest.add_discovery { "rspec" }
Teaching Autotest about our custom directory structure
In order to do this, we have two options. If we want to use the same structure for all our projects, we can put a file in our home directory, otherwise we can add custom files to each project directory.
Step 2: create a file .autotest with the following code:
class Autotest::Rspec < Autotest
remove_method :consolidate_failures
def consolidate_failures(failed)
filters = new_hash_of_arrays
failed.each do |spec, trace|
if trace =~ /\n(\.\/)?(.*example\.rb):[\d]+:/
filters[$2] << spec
end
end
return filters
end
remove_method :add_options_if_present
def add_options_if_present # :nodoc:
File.exist?("examples/spec.opts") ? "-O examples/spec.opts " : ""
end
end
Autotest.add_hook(:initialize) {|at|
%w{.git .svn .hg .swp .DS_Store ._* tmp}.each do |exception|
at.add_exception(exception)
end
at.clear_mappings # take out the default (test/test*rb)
at.add_mapping(%r%^(examples|third_party)/.*_example.rb$%) { |filename, _|
filename
}
at.add_mapping(%r%^lib/(.*)\.rb$%) { |_, m|
["examples/#{m[1]}_example.rb"]
}
at.add_mapping(%r%^examples/(example_helper|shared/.*)\.rb$%) {
at.files_matching %r%^(examples|third_party)/.*_example\.rb$%
}
#nil
}
Let’s take it piece by piece:
def consolidate_failures(failed)
filters = new_hash_of_arrays
failed.each do |spec, trace|
if trace =~ /\n(\.\/)?(.*example\.rb):[\d]+:/
filters[$2] << spec
end
end
return filters
end
This method tells Autotest which test files have failed by pulling the file names out of the test output. Since autospec looks for *_spec.rb files, it will not find our custom files. I use *_example.rb filenames, so you should replace that part of the regex with whatever you use.
def add_options_if_present # :nodoc:
File.exist?("examples/spec.opts") ? "-O examples/spec.opts " : ""
end
This method tells Rspec where to find an option file. Again, replace the details with whatever you use.
In Autotest.add_hook(:initialize):
%w{.git .svn .hg .swp .DS_Store ._* tmp}.each do |exception|
at.add_exception(exception)
end
This tells Autotest to ignore certain files that are irrelevent to testing
at.clear_mappings
This tells Autotest not to use any other mappings i.e. the default test/test*rb, or Rspec’s spec/*_spec.rb
at.add_mapping(%r%^(examples|third_party)/.*_example.rb$%) { |filename, _|
filename
}
at.add_mapping(%r%^lib/(.*)\.rb$%) { |_, m|
["examples/#{m[1]}_example.rb"]
}
at.add_mapping(%r%^examples/(example_helper|shared/.*)\.rb$%) {
at.files_matching %r%^(examples|third_party)/.*_example\.rb$%
}
This is the meat of the customization. It tells Autospec, based on which files have changed, which tests to run. Replace my regexes with your custom matches.
The end
Once you have added both these files, running autospec from your project directory will work as it should!
Recent Comments