Automatically Run Tests While Solving Exercism.io Exercises

TL;DR:

watch( %r{^exercises/(.*)/(.*)\.rb$} ) { |matches| "exercises/#{matches[1]}/#{matches[1]}_test.rb" }
  • Run bundler exec guard

Skip down to the Setup Steps section below for more details.


Background: Learning Ruby

One of the things I love the most about Automattic is how it’s focused on investing in people for the long term. Part of that is the perk of getting a 2-3 month sabbatical every five years. I started my first one last month, and have been playing around with some technologies that I don’t normally work with in my day-to-day job, to get some exposure to how other communities have approached problems, in hopes of becoming a more well-rounded developer.

One of the things I’ve been learning is the Ruby language (and of course, the Rails framework. Side note: The Rails Doctrine is a very good read, regardless of whether or not you agree with everything in it). I started by taking some of the Treehouse courses, but as an experienced developer, I found them frustratingly slow. I felt like I was spending hours learning things that I could easily pick up in a few minutes in a different format.

So, I decided to try a different approach, and read through the Learn X in Y Minutes summary of Ruby’s syntax and core API. That gave me enough familiarity with the basics to start playing around with some small scripts.

I think I learn best by doing, and using each obstacle that comes up as an opportunity to find the answers to all the questions it raises, so I wanted to have some real(ish) problems to solve. My search led me to Exercism.io, which turned out to be a great resource. They have dozens of sample exercises available for Ruby (and many other languages), along with solutions that you can review after you’ve completed your own. Seeing how other people solved the problem has been a very helpful way to improve on my solutions, and learn more in the process.

That brief Learn X in Y Minutes introduction to the language, the practices exercises, and the Ruby documentation, have turned out to be a much better way for me to learn than sitting through video courses with minimal hands-on exercises, and overly-simplistic standardized tests.

Automatically Running Tests

That leads me to the catalyst for writing this post, though. I found it tedious to have to manually execute the unit tests throughout the development process; I wanted them to run automatically every time I saved a file, like they do when working on WordPress Core and Calypso.

I looked around and, of course, there were several options for accomplishing this. I chose to use Guard along with Guard::Minitest, since it fit the workflow I was looking for, and felt similar to what I’ve implemented before with Grunt.

Not surprisingly, I had to struggle with the Guardfile for awhile in order to get things working, but I finally did. Here’s what I came up with:

Setup Steps

  • git clone git@github.com:exercism/ruby.git exercism-ruby
  • Add gem 'guard' and gem 'guard-minitest' to your Gemfile, then run bundle
    • In this case it’s necessary to explicitly add guard, even though normally the dependency would handled by bundler.
  • Add this to your Guardfile:
guard :minitest, test_folders: 'exercises', test_file_patterns: '*_test.rb', all_on_start: false do
    # This is saying watch for any changes to any .rb file in any subdirectory of exercises
    # Then modify the name of the file that gets passed to minitest, so that it just passes exercises/foo/foo_test.rb
    watch( %r{^exercises/(.*)/(.*)\.rb$} ) { |matches| "exercises/#{matches[1]}/#{matches[1]}_test.rb" }
end
  • From inside the exercism-ruby folder, run bundler exec guard
    • If your changes aren’t being detected, you may need to use the -p flag to enable polling,

Customizing Minitest Output

If you also want to have color output, or hide the verbose messages for skipped tests, you can follow these steps:

  • Add gem 'minitest-reporters' to your Gemfile and run bundler
  • Add this to all of your exercism-ruby/exercises/*/*_test.rb files:
    • There’s probably a more DRY way to do it, but I haven’t spent the time to get that working yet.
require 'minitest/reporters'

minitest_options = { color: true, detailed_skip: false }
Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new( minitest_options )]

Leave a Reply

Your email address will not be published. Required fields are marked *