I started using Watchr in a couple of projects. An interesting use of Watchr is to check on your tests. I have been using this script with great success so far:

test.watchr

1 def failed
2 system("growlnotify —name adgear-reporting-tests —image /Applications/Mail.app/Contents/Resources/Caution.tiff -m ‘Oops, tests failed’")
3 system("say ‘failed’")
4 end
5
6 def succeeded
7 system("growlnotify —name adgear-reporting-tests -m ‘Green tests’")
8 system("say ‘pass’")
9 end
10
11 watch((lib|test)/.+\.(js|rb)) do |_|
12 system("rake")
13 $?.success? ? succeeded : failed
14 end

Notice I’m using Mac OS X application paths, but it works just fine for me. And the regular “pass” and “failed” messages keep me abreast of what’s going on. But this style of continuous testing only works when your tests take a few seconds at most. When my tests take much longer than that, I start fiddling, opening Google Reader, checking news, whatever.

Your mileage may vary.

Piston 2.0.8 is here. This is a minor bugfix release:

  • piston status with no path would not check any status. Thanks to Michael Grosser for the heads up;
  • The ActiveSupport gem deprecated require “activesupport” in favor of “active_support”. Thanks for Michael for reporting this as well;
  • Scott Johnson reported and fixed a problem where a git mv would fail because a parent directory was missing.

Thanks to all contributors!

Installing


1 $ gem install piston

At yesterday’s Montreal.rb I presented Nestor, an autotest-like framework. This is it’s official release announcement.

Nestor is different in that it uses an explicit state machine, namely Aaron Pfeifer‘s StateMachine. Nestor also uses Martin Aumont’s Watchr to listen to filesystem events. But the biggest difference is that the default Rails + Test::Unit is a forking test server. Nestor will load the framework classes—ActiveRecord, ActionController, ActionView, plugins and gems—only once. This saves a lot of time on the aggregate versus running rake everytime.

Nestor's state diagram with events denoting success or failure of a run, and states such as green, running_all or run_focused_pending.
Click for larger version

This release of Nestor is 0.2 quality: it’s not ready for large projects. It only supports Rails + Test::Unit, probably doesn’t run on 1.9 or JRuby, but it’s a solid foundation for going further. In the coming days, I will blog on the internals of Nestor and how StateMachine allowed me to add plugins with very little effort.

Installation


1 $ gem install nestor
2 $ cd railsapp
3 $ # edit config/environments/test.rb to set cache_classes to false
4 $ nestor

I already have a plugin that enables Growl notifications. Install and use:


1 $ gem install nestor_growl
2 $ cd railsapp
3 $ nestor start —require nestor/growl

The —require option is where plugins are loaded. This is an Array of files Nestor will require on startup.

Notes

You must set cache_classes to false in test mode for now. This is a limitation of how Rails boots. With cache_classes set to true, Rails will load the controllers and models when it boots. Since this happens before forking, the code under test would never get reloaded. Did I say it was 0.2 quality?

Since yesterday, I read a bit more about autotest. I stumbled on Getting Started with Autotest which you should read immediately. The article talks about setting up your plugins and doing all kinds of interesting things.

Getting more proficient with my tools now!

You know, I write controller tests day in, day out. I’m used to seeing this:


1 should "be valid"
2 assert assigns(:user).valid?
3 end

So, when I write a Rake task and I render some data, I need my assigns:

lib/tasks/ssl.rake

1 namespace :ssl do
2 task :build => :environment do
3 rm_rf "config/sites/"
4 mkdir "config/sites"
5
6 Dir["config/ssl/*"].each do |path|
7 site = File.basename(path)
8 parts = site.split(".")
9
10 puts "Processing #{site}"
11 assigns = {
12 :wildcard_domain => parts[0] == "",
13 :domain_name => parts[-2..-1].join("."),
14 :ssl_key_file_path => File.expand_path(File.join(Dir.pwd, path, "#{site}.key")),
15 :ssl_certificate_file_path => File.expand_path(File.join(Dir.pwd, path, "#{site}.crt"))}
16
17 next "Skipping #{site}: #{assigns(:ssl_key_file_path)} does not exist or is unreadable" unless File.file?(assigns(:ssl_key_file_path))
18 next "Skipping #{site}: #{assigns(:ssl_certificate_file_path)} does not exist or is unreadable" unless File.file?(assigns(:ssl_certificate_file_path))
19
20 view = ActionView::Base.new([File.dirname(
_FILE__)], assigns)
21
22 File.open(File.join("config", "sites", "#{site}.ssl.conf"), "w") do |site_config|
23 puts "Writing config/sites/#{site}.ssl.conf"
24 site_config.write view.render("ssl.conf.erb")
25 end
26 end
27
28 view = ActionView::Base.new([File.dirname(FILE)], {})
29 File.open(File.join("config", "sites", "default.conf"), "w") do |site_config|
30 site_config.write view.render("default.conf.erb")
31 end
32 end
33 end

Can you spot the error on lines 17 and 18? Go on, I’ll wait…

You want some help? Here’s the backtrace:


1 * Invoke ssl:build (first_time)
2 *
Invoke environment (first_time)
3 * Execute environment
4 *
Execute ssl:build
5 rm -rf config/sites/
6 mkdir config/sites
7 Processing _.xlsuite.com
8 rake aborted!
9 undefined method `assigns’ for #<Object:0×389a0>
10 /Users/francois/Documents/work/xlsuite-stable.svn/lib/tasks/ssl.rake:17

Yes, took me a while too. I was calling a method named assigns, not using the Hash named assigns.

Habit, when you take hold of me…

I’m just setting up Continuous Integration for all of our branches on XLsuite.com. Doing a search for “cruisecontrol.rb” on Google unearthed: Ci For The Web 2.0 Guy Or Gal. I’m running the steps right now on my RimuHosting VPS.

Things are progressing along quite rapidly. I’ll say more when I’m done.

One thing’s for sure, it’s not obvious what’s going on when a test fails because of cookies. For that reason, I shied away from using cookies significantly.

Anyway, here’s a failure I was getting:



1 1) Failure:
2 test_create_session_with_right_username_and_password_and_remember_me(CreateSessionsControllerTest)
3 [test/functional/sessions_controller_test.rb:57:in `test_create_session_with_right_username_and_password_and_remember_me’
4 /home/francois/src/config/../vendor/plugins/mocha/lib/mocha/test_case_adapter.rb:19:in `run’]:
5 No :auth_token cookie in the response
6 -
7 auth_token:
8


1 1) Failure:
2 test_create_session_with_right_username_and_password_and_remember_me(CreateSessionsControllerTest)
3 [test/functional/sessions_controller_test.rb:57:in `test_create_session_with_right_username_and_password_and_remember_me’
4 /home/francois/src/config/../vendor/plugins/mocha/lib/mocha/test_case_adapter.rb:19:in `run’]:
5 No :auth_token cookie in the response
6 -
7 auth_token:
8 – cookie value


Turns out the @response.cookies Hash is not indifferent. You really have to use a String to get to the content:


1 def test_cookie_set
2 assert_equal ["some value"], response</span>.cookies[<span class="s"><span class="dl">&quot;</span><span class="k">auth_token</span><span class="dl">&quot;</span></span>], <span class="no">3</span> <span class="s"><span class="dl">&quot;</span><span class="k">this will succeed</span><span class="dl">&quot;</span></span> <span class="no">4</span> assert_equal [<span class="s"><span class="dl">&quot;</span><span class="k">some value</span><span class="dl">&quot;</span></span>], <span class="iv">response.cookies[:auth_token],
5 "this will always fail"
6 end

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:

lib/tasks/find_missing_fixtures.rake

1 # Find missing fixtures declarations from Rails tests
2 # Code by Francois Beausoleil (francois@teksol.info)
3 # Released in the public domain. Do as you wish.
4 desc "Finds missing fixtures declarations from your tests"
5 task :find_missing_fixtures do
6 state = :find_test_case
7 test_case = nil
8
9 Dir[test//_test.rb].each do |file|
10 File.open(file, r) do |f|
11 f.each do |line|
12 case state
13 when :find_fixture
14 case line
15 when /def test_/
16 printf "%s: %s\n", file, test_case
17 state = :find_test_case
18 when /fixtures/
19 state = :find_test_case
20 when /class (\w) < Test::Unit::TestCase$/
21 test_case = $1
22 state = :find_test_case
23 end
24
25 when :find_test_case
26 case line
27 when /class (\w) < Test::Unit::TestCase$/
28 test_case = $1
29 state = :find_fixture
30 end
31 end
32 end
33 end
34 end
35 end

Run it like this:


1 $ rake find_missing_fixtures
2 (in D:/wwwroot/wpul.staging.teksol.info)
3 test/unit/name_test.rb: NameTest
4 test/unit/application_helper_test.rb: ApplicationHelperTest
5 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…

Search

Your Host

A picture of me

I am François Beausoleil, a Ruby on Rails and Scala developer. During the day, I work on Seevibes, a platform to measure social interactions related to TV shows. At night, I am interested many things. Read my biography.

Top Tags

Books I read and recommend

Links

Projects I work on

Projects I worked on