Separating test code

Here’s one trick that has been used a lot in the early days of Ruby development:

def leap_year?(year)
  year % 400 == 0 or year % 100 != 0 and year % 4 == 0
end

if $0 == __FILE__
  puts "2001: #{leap_year?(2001)}"
  puts "1900: #{leap_year?(1900)}"
  puts "2000: #{leap_year?(2000)}"
  puts "2004: #{leap_year?(2004)}"
end

If you store this code in a file leap_year.rb and execute it with ruby leap_year.rb then you’ll get the same output as above.

However, if you write a bigger program which requires this file by require "leap_year" (so it can include your method definition, and use it somewhere else) then you would not get this output.

You can try this out quickly on the command line:

$ ruby -I -r leap_year.rb -e "p leap_year?(1996)"
true

The flag -r tells Ruby to require your file. The flag -I tells it to look in the current directory to load the file. The flag -e tells it to execute the given code. This way you don’t have to create a new file in order to try this out.

As you can see it now won’t execute your “test”, and thus won’t output 2004: true again.

That is cool. We’ve just separated our test code from the implementation, i.e. we can run the tests separately, if we want to. In turn it won’t run the test code when we just require the file, so we can use the method for something else.

How does this work though?

The variables $0 and __FILE__ are rather arcane, and they were inspired by other languages that existed when Matz designed Ruby in the 90s, especially Perl, in this case.

The variable $0 is a global variable (hence the dollar sign $) that holds the name of the Ruby file that was given on the command line. So if you run ruby leap_year.rb, then $0 will contain "leap_year.rb"

The variable __FILE__ on the other hand is defined in every Ruby file, and contains the file name of this exact file. If we execute ruby leap_year.rb then these two names will be the same. If we execute any other ruby code that requires the file leap_year.rb though, then they will not be the same.

We can further improve our test code by making it less repetitive, and abstract it:

def leap_year?(year)
  year % 400 == 0 or year % 100 != 0 and year % 4 == 0
end

if $0 == __FILE__
  [2001, 1900, 2000, 2004].each do |year|
    puts "#{year}: #{leap_year?(year)}"
  end
end

Exercise: Try going to back to the Ruby for Beginners book and add some tests to some of the exercises you did.