Mock System Call in Ruby

Mock system call in ruby

%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

How do I mock or override Kernel.system?

If you are talking about unit tests and use Rspec, you should be able to do it like this:

Kernel.should_receive(:system)

or a little more loose:

Kernel.stub(:system)

More info: https://www.relishapp.com/rspec/rspec-mocks/v/2-13/docs/message-expectations/expect-a-message

How do I stub/mock a call to the command line with rspec?

Here is a quick example I made. I call ls from my dummy class. Tested with rspec

require "rubygems"
require "spec"

class Dummy
def command_line
system("ls")
end
end

describe Dummy do
it "command_line should call ls" do
d = Dummy.new
d.should_receive("system").with("ls")
d.command_line
end
end

How to test system calls from the rake task

Note that Kernel is a module which is included into every ruby Object. And Kernel#system is an instance method (not a class method).

One solution (although discouraged by rspec maintainers) is to use "Any instance":

it "runs 'bundle exec rspec spec'" do
expect_any_instance_of(Kernel).to receive(:system).with "bundle exec rspec spec"
Rake::Task[:test].invoke
end

In order to use regular expect or allow, you will need the actual instance of the object which is receiving the message. For a Rake task this will be cumbersome (although not impossible - see this question) - they are executed in the toplevel context.

I would propose that you encapsulate your system calls into utility class methods and expect those. It would make the testing easier and you have explicit classes & instances to work with.

Is there a way to set the value of $? in a mock in Ruby?

EDIT: Using Dennis Williamson's suggestion:

command = "(exit 21)"

and use if $?.exitstatus == 0 instead of if $? == 0



Related Topics



Leave a reply



Submit