Testing invocation of external executables, %x{}, `

If you ever found yourself trying to write tests for code that invokes an external executable, such as:

  class Beef < ActiveRecord::Base
    def diskspace(flags = 'sh')
      `du -#{flags} .`.split.first
    end
  end

…and wondered how to write a spec that ensures the du command is called with the right options, you might have tried something like:

  before do
    @beef = Beef.new
  end

  it "has diskspace with humanized multipliers" do
    Kernel.should_receive(:`).with("du -h .")
    @beef.diskspace('h')
  end

That doesn’t work.

After some headscratching and calls for help on the ruby-talk mailing list, I learned how the Kernel#` method is not being called directly. The Ruby object hierarchy has Object at the top-level, and thus all your objects eventually descends from Object. As Object mixes in Kernel, all your objects have all the methods defined in that module, so it’s not Kernel that receives your call to “`”, but it’s the current ‘self’. In the example above it’s the @beef instance.

Consider:

  class Sheep
    def mytick(arg)
      puts "tickety-tick: #{arg.inspect}"
    end
    alias :"`" :mytick

    def tick_it!
      `cheese`
    end
  end

  Sheep.new.tick_it!

  $ ruby sheep.rb
  tickety-tick: "cheese"

So, to test the original code, here’s the right way:

  it "has diskspace with humanized multipliers" do
    @beef.should_receive(:`).with("du -h .")
    @beef.diskspace('h')
  end

All of the above also applies to the “%x[]”-syntax (just an alias for Kernel#`).

Kudos to Brian Chandler for helping out on this, and happy hacking!

Leave a Reply

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