How do I mock or override Kernel.system?
Asked Answered
F

4

5

How would be the correct way to mock or override the Kernel.system method so that when called with:

system("some command")

instead of executing the command, it executes some predefined code?

I tried adding the following to my Test class:

module Kernel
    def system
        puts "SYSTEM CALL!!"
    end
end

But it did not work as expected, instead, the system call was run when executing a test.

Faucal answered 20/5, 2013 at 17:58 Comment(0)
O
7

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

Ovarian answered 20/5, 2013 at 18:13 Comment(4)
The link I gave you was from the official rspec docs: relishapp.com/rspec. You can also check out betterspecs.org - it gives a number of best practices on how to use rspec properly.Ovarian
Yes, I am talking about Unit tests, but unfortunately the code is not using rspec but simple Test UnitFaucal
In that case you should take a look at: github.com/freerange/mocha - Kernel.expects(:system).returns(true)Ovarian
Does the should_receive(:method_name) stub the method so that it isn't executed? Or do you need to use .stub(:method_name) to prevent it from being executed?Evoke
E
14

In some cases doing expect(Kernel).to receive(:system) is not enough.

Consider this example:

foo_component.rb

class FooComponent
  def run
    system('....')
  end
end

foo_component_spec.rb

require 'spec_helper'

describe FooComponent do
  let(:foo_component) { described_class.new }

  describe '#run' do
    it 'does some awesome things' do
      expect(Kernel).to receive(:system).with('....')
      foo_component.run
    end
  end
end

It will not work. This is because Kernel is a module and Object (parent class) is mixes in the Kernel module, making all Kernel method available in "global" scope.

This is why proper tests should looks like this:

require 'spec_helper'

describe FooComponent do
  let(:foo_component) { described_class.new }

  describe '#run' do
    it 'does some awesome things' do
      expect(foo_component).to receive(:system).with('....')
      foo_component.run
    end
  end
end
Elaterin answered 14/7, 2016 at 8:13 Comment(0)
O
7

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

Ovarian answered 20/5, 2013 at 18:13 Comment(4)
The link I gave you was from the official rspec docs: relishapp.com/rspec. You can also check out betterspecs.org - it gives a number of best practices on how to use rspec properly.Ovarian
Yes, I am talking about Unit tests, but unfortunately the code is not using rspec but simple Test UnitFaucal
In that case you should take a look at: github.com/freerange/mocha - Kernel.expects(:system).returns(true)Ovarian
Does the should_receive(:method_name) stub the method so that it isn't executed? Or do you need to use .stub(:method_name) to prevent it from being executed?Evoke
E
5

If it is within a class, kernel is mixed in. So you would just mock it as if it was part of the object.

E.g.

expect(subject).to receive(:system).and_return(foo)
Eulalia answered 20/11, 2018 at 21:33 Comment(0)
L
4

Since this question was asked, RSpec 3 has come out with a new syntax, where you would write this:

expect(Kernel).to receive(:system)

If your code checks whether or not the system call was a success, you can specify the result like this:

expect(Kernel).to receive(:system).and_return(true)

The loose version:

allow(Kernel).to receive(:system).and_return(true)
Lacrosse answered 18/1, 2015 at 11:36 Comment(1)
FYI Using ruby 2.3.0 and rspec-core 3.4.2, I had to use Kernel.system(...) instead of just system(...) for the mock to pass.Maebashi

© 2022 - 2024 — McMap. All rights reserved.