Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Runs tests as they change, in parallel

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.

test-loop - Continuous testing for Ruby with fork/eval

test-loop is a fast continuous testing tool for Ruby that continuously detects and tests changes in your Ruby application in an efficient manner, whereby it:

  1. Absorbs the test execution overhead into the main Ruby process.
  2. Forks to evaluate your test files directly and without overhead.

It relies on file modification times to determine what parts of your Ruby application have changed and then uses a simple lambda mapping function to determine which test files in your test suite correspond to those changes.


  • Tests changes in your Ruby application; does not run all tests every time.

  • Reabsorbs test execution overhead if the test or spec helper file changes.

  • Mostly I/O bound, so you can have it always running without CPU slowdowns.

  • Supports any testing framework that (1) reflects failures in the process' exit status and (2) is loaded by your application's test/test_helper.rb or spec/spec_helper.rb file.

  • Configurable through a .test-loop file in your current working directory.

  • Implemented in less than 60 (SLOC) lines of code! :-)


As a Ruby gem:

gem install test-loop

As a Git clone:

git clone git://


If installed as a Ruby gem:


If installed as a Git clone:

ruby bin/test-loop


  • Press Control-Z or send the SIGTSTP signal to forcibly run all tests, even if there are no changes in your Ruby application.

  • Press Control-\ or send the SIGQUIT signal to forcibly reabsorb the test execution overhead, even if its sources have not changed.

  • Press Control-C or send the SIGINT signal to quit the test loop.


test-loop looks for a configuration file named .test-loop in the current working directory. This configuration file is a normal Ruby script which can define the following instance variables:

  • @overhead_file_globs is an array of file globbing patterns that describe a set of Ruby scripts that are loaded into the main Ruby process as overhead.

  • @reabsorb_file_globs is an array of file globbing patterns that describe a set of files which cause the overhead to be reabsorbed whenever they change.

  • @source_file_to_test_file_mapping is a hash that maps a file globbing pattern describing a set of source files to a lambda function yielding a file globbing pattern describing a set of test files that need to be run. In other words, whenever the source files (the hash key; left-hand side of the mapping) change, their associated test files (the hash value; right-hand side of the mapping) are run.

    For example, if test files had the same names as their source files but the letters were in reverse order, then you would add the following to your .test-loop file:

    @source_file_to_test_file_mapping = {
      '{lib,app}/**/*.rb' => lambda do |path|
        extn = File.extname(path)
        name = File.basename(path, extn)
        "{test,spec}/**/#{name.reverse}#{extn}" # <== notice the reverse()
  • @after_test_execution is a lambda function that is executed after tests are run. It is passed three things: the status of the test execution subprocess, the time when the tests were run, and the list of test files that were run.

    For example, to get on-screen-display notifications through libnotify, add the following to your .test-loop file:

    @after_test_execution = lambda do |status, ran_at, files|
      if status.success?
        result, icon = 'PASS', 'apple-green'
        result, icon = 'FAIL', 'apple-red'
      system 'notify-send', '-i', icon, "#{result} at #{ran_at}", files.join("\n")


See the bin/test-loop file.

Something went wrong with that request. Please try again.