20

Know of a way to mock %[]? I'm writing tests for code that makes a few system calls, for example:

def log(file)
  %x[git log #{file}]
end

and would like to avoid actually executing system calls while testing this method. Ideally I'd like to mock %x[..] and assert that the correct shell command is passed to it.

dstnbrkr
  • 4,305
  • 22
  • 23

5 Answers5

19

%x{…} is Ruby built-in syntax that will actually call Kernel method Backtick (`). So you can redefine that method. As backtick method returns the standard output of running cmd in a subshell, your redefined method should return something similar to that ,for example, a string.

module Kernel
    def `(cmd)
        "call #{cmd}"
    end
end

puts %x(ls)
puts `ls`
# output
# call ls
# call ls
pierrotlefou
  • 39,805
  • 37
  • 135
  • 175
14

Using Mocha, if you want to mock to following class:

class Test
  def method_under_test
    system "echo 'Hello World!"
    `ls -l`
  end
end

your test would look something like:

def test_method_under_test
  Test.any_instance.expects(:system).with("echo 'Hello World!'").returns('Hello World!').once
  Test.any_instance.expects(:`).with("ls -l").once
end

This works because every object inherits methods like system and ` from the Kernel object.

Jippe
  • 714
  • 6
  • 14
  • Using `should_receive` instead of `expects` – Peter Ehrlich Dec 07 '13 at 00:11
  • 1
    Doesn't work with the current version. Minitest::UnexpectedError: NoMethodError: undefined method `any_instance' for Test:Module; However, it worked if I called it on the object. – Velizar Hristov Nov 01 '15 at 11:57
  • Yes, the pitfall is that `expects(:\`)` should be called on the class which makes the system call, but not on `Kernel`. – Jing Li Nov 04 '16 at 07:08
  • Same here Velizar, and thanks for the comment. Adding that I am using Ruby 2.1.5 with Rails 3.2.22.1. – Pysis Nov 16 '16 at 20:59
3

I don't know of a way to mock a module, I'm afraid. With Mocha at least, Kernel.expects doesn't help. You could always wrap the calling in a class and mock that, something like this:

require 'test/unit'
require 'mocha'

class SystemCaller
  def self.call(cmd)
    system cmd
  end
end

class TestMockingSystem < Test::Unit::TestCase
  def test_mocked_out_system_call
    SystemCaller.expects(:call).with('dir')
    SystemCaller.call "dir"
  end
end

which gives me what I'd hope for:

Started
.
Finished in 0.0 seconds.

1 tests, 1 assertions, 0 failures, 0 errors
Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
0

How about logging it to a text file, or outputting it to your console?

def log(file)
  puts "git log #{file}"
end
JAL
  • 21,295
  • 1
  • 48
  • 66
-2

Can't you just ovverride the function with a method that returns true when it gets the command?

CodeJoust
  • 3,760
  • 21
  • 23