limiting scope of autotest 17
If you use autotest with rspec or test/unit, you’ve probably had this experience (or one like it):
You want to add some new behaviour to a model object, so you write a spec, watch it fail, make it pass, and then wait until the entire spec suite runs. Even if you’ve got a fast-running suite, this can be painful sometimes.
Wouldn’t it be great if you could limit the scope of what directories autotest observes? Well it turns out that you can! Recent releases of ZenTest include a find_directories attribute on the autotest object. Just add this to your .autotest file:
Autotest.add_hook :initialize do |at|
unless ARGV.empty?
at.find_directories = ARGV.dup
end
end
and then you can say:
autotest app/models spec/models
and it will only observe those directories. This is nice and flexible, but I find that most of the time I’m wanting pairs like that: app/models and spec/models, or app/views/accounts and spec/views/accounts. In that case, I’d really like to just say:
autotest models
To accomplish that you can do this to the hook instead:
Autotest.add_hook :initialize do |at|
unless ARGV.empty?
at.find_directories = ["spec/#{ARGV.first}","app/#{ARGV.first}"]
end
end
Want the best of both worlds? Try this:
Autotest.add_hook :initialize do |at|
unless ARGV.empty?
at.find_directories = ARGV.length == 1 ? ["spec/#{ARGV.first}","app/#{ARGV.first}"] : ARGV.dup
end
end
The only limitation of this is that it’s based on directories, not files. Once in a while, when I’m bootstrapping a new object, I’ll keep the examples and the implementation in the same file until I’ve got things fleshed out a bit the object is ready to play nice with others. In that case, I might like to just point autotest to that one file. I started working on a patch for this for ZenTest, but I’m not sure it’s worth the extra effort. What do you think?
Regardless – happy auto-exemplifying!
RSpec-1.1.3 and ZenTest-3.9.1 6
ZenTest’s last two releases are not compatible with previous versions of RSpec. This is good news because Autotest now exposes better extension points for subclasses like those that ship with RSpec. Before, RSpec had to monkey patch Autotest to control the mappings of specs to files to run, and the list of files/directories to ignore. Now RSpec gets to use public methods (instead of instance variables) and documented hooks to do it’s work.
In the long run, this will keep things more flexible for both RSpec and ZenTest. In the short run, the catch for you is that you have to use compatible versions of RSpec and ZenTest. They are:
| RSpec version | ZenTest version |
|---|---|
| <= 1.1.1 | <= 3.7.x |
| 1.1.2 | 3.8.x |
| 1.1.3 | 3.9.x |
RSpec-1.1.2 and ZenTest-3.8.0 11
The RSpec-1.1.2 release includes changes to keep RSpec compatible with autotest in ZenTest-3.8.0. This new ZenTest release boasts an improved cascading configuration model that works well for subclasses (like those that ship with RSpec) and allows users to override the mappings of specs (or tests) to code as well as the list of files that get ignored by autotest.
To support this, Autotest now loads the following files in the following order:
Autotest
AutotestSubClass
~/.autotest
./.autotest
This allows RSpec (or any other library) to override defaults set in Autotest, and then provides users both generic (~/.autotest) and project specific (./.autotest) control over the mappings and exceptions.
How can you take advantage of this?
When autotest begins to run, it calls its :initialize hook. This hook is exposed by the add_hook method. You can use this to access the mappings and exceptions using the following methods on Autotest:
clear_mappings()
add_mapping(regexp, proc)
remove_mapping(regexp)
clear_exceptions()
add_exception(string)
remove_exception(string)
add_mapping
The add_mapping method adds a key/value pair to a hash that maps regexps to procs. Whenever autotest senses that a file is touched, it looks for the regexp that matches the file name and the runs all the files returned by the associated proc.
Imagine you’re working on a shopping cart app. You have some currency conversion behaviour in a Product model that you’d like to extract to an acts_as_currency plugin, and you want autotest to observe the process. You might add a mapping like this to .autotest:
Autotest.add_hook :initialize do |at|
at.add_mapping(%r%^plugins/acts_as_currency/lib/.*\.rb$%) {
at.files_matching %r%^spec/models/product_spec\.rb$% +
at.files_matching %r%^plugins/acts_as_currency/spec/.*_spec\.rb$%
}
end
In this case, a change to any of the files in the plugin’s lib directory would cause all the plugins specs to run, as well as the spec for the Product model.
add_exception
The add_exception method adds paths to a list of paths that Autotest ignores.
I like to run autotest in verbose mode (autotest -v) because it tells me when I change a file that it doesn’t know what to do with. The drawback is that it wants to tell me every time I commit because files in the .svn/.hg/.git directories change. So I’ve got these all listed as exceptions in my ~/.autotest file, along with assorted others:
Autotest.add_hook :initialize do |at|
%w{.svn .hg .git}.each {|exception|at.add_exception(exception)}
end
Note that autotest compiles this list to a Regexp with no anchors, so .hgignore and .gitignore would also get ignored in this case.
Cascading config and granular control
One of the coolest changes in ZenTest-3.8.0 is that autotest loads both ~/.autotest and ./.autotest. So now you can have the hooks you like on every project (like growl notifation) all in one place and still have project specific settings.
This also allows you to set up global mappings/exceptions and modify them at the project level. See Autotest’s RDoc for more info.
web_spec: ghost of spec:ui past
Ryan Davis just posted about a problem he helped to diagnose that relates back to an earlier version of Spec::Ui (which ships with RSpec).
The first version of Spec:Ui was called web_spec, and it (unfortunately and erroneously) got installed as a gem when you installed RSpec 0.7.5.
If you have this in your gems directory, kindly send it on its merry way. It’s broken, and will break your stuff (read Ryan’s blog post for details).
rspec and autotest 10
At RailsConf 2007 in Portland, I had the privilege of sitting down w/ Ryan Davis and working with him to improve the runtime relationship between RSpec and Autotest. David Goodlad was at the table as well, and helped me work out some of the mappings so autotest runs the right examples when you change application code.
The result is that with RSpec >= 1.0.3 and ZenTest >= 3.6.0, you can now use Autotest with RSpec on your Ruby projects, Rails or otherwise, simply by typing “autotest” in the project root. No additional plugins necessary. Sweet.
One gotcha: Some have reported that when an example fails, autotest keeps running it over and over again until you get it to pass. This is due to a conflict between RSpec’s and autotest’s mechanisms for narrowing down the set of files to run. This is easily resolved by removing the following lines from spec/spec.opts:
--format failing_examples:previous_failures.txt --example previous_failures.txt
If you run ‘script/generate rspec’, these lines will not be included in the generated spec.opts file. Otherwise you can just delete them yourself.
RSpec-0.9.1 and Autotest (ZenTest-3.5.2) 6
Autotest (part of ZenTest) now supports RSpec. This is fantastic news! For those of you who do not know about autotest, it is a program that runs in the background while you are writing your tests and code. Each time you make a change it automatically reruns your tests – and now your specs, too! This is a powerful addition to the TDD/BDD experience.
Recent releases of both tools overlapped a bit so there are changes in RSpec-0.9.1 that are not reflected yet in ZenTest. Also, while ZenTest-3.5.2 supports Spec::Rails, RSpec’s Ruby on Rails plugin, it does not support non-Rails Ruby projects.
I’ve submitted a patch to the ZenTest project which addresses both of these issues. Until the patch is applied, or the issues are addressed in some other way, you can apply it yourself to get autotest working with RSpec for Rails and other projects. These steps work on a mac. I assume that the commands are quite similar for Linux and Cygwin users.
- Go to http://rubyforge.org/frs/?group_id=419 and download ZenTest-3.5.2.tgz
- Unpack the tar and
tar zxvf ZenTest-3.5.2.tgz cd ZenTest-3.5.2
- Get and install the patch
curl -O http://blog.davidchelimsky.net/files/ZenTest-3.5.2-rspec.patch patch -p0 < ZenTest-3.5.2-rspec.patch
- Build and install the gem
rake gem sudo gem install pkg/ZenTest-3.5.2.gem
Once you’ve built and installed the patched gem, you run autotest as normal. Stand in the root of your project and say:
autotest
If you have a spec directory at the root of your project, autotest will load up rspec_rails_autotest for Rails projects and rspec_autotest for everything else.
To quote Josh Knowles, Happy (Auto)Specing!