I cannot find a workaround other than disabling the cop for Kernel methods, such as system and sleep.
If there is any, I'd love to know it.
Some highly upvoted answers on StackOverflow, as for example RSpec: stubbing Kernel::sleep? suggest doing exactly what the cop treats as a violation.
I created a simple sandbox repository for just that case, in case you'd like to play around. In short, there is an example:
Tested code:
class Git
def installed?
!`which git`.delete("\n").empty?
end
end
Test:
RSpec.describe Git do
subject(:instance) { described_class.new }
let(:git_installation_path) { "/usr/bin/git\n" }
before { allow(instance).to receive(:`).with('which git').and_return(git_installation_path) }
describe '#installed?' do
context 'when installed' do
it { expect(instance.installed?).to eq(true) }
end
context 'when not installed' do
let(:git_installation_path) { "\n" }
it { expect(instance.installed?).to eq(false) }
end
end
end
Right now, I'm forced to disable the cop project-wide, and I think that simple whitelist that'd enable stubbing a few of Kernel methods that I use would suffice.
If I'm wrong and there's a way to do that by stubbing Kernel itself, please share it. As far as I've tried replacing the before statement in the code above with this:
before { allow(Kernel).to receive(:`).with('which git').and_return(git_installation_path) }
did not work.
Thanks for your time, you're doing a great job with the style guide.
I assume the following might work for you:
before { allow_any_instance_of(Object).to receive(:`).with('which git').and_return(git_installation_path) }
One approach would be to wrap the system calls in your own class or module, e.g.:
module SystemCommand
def self.call(command)
`#{command}`
end
end
which you can then easily stub:
allow(SystemCommand).to receive(:call).with("which git").and_return("/usr/bin/git\n")
I would avoid globally stubbing methods on Object or Kernel since it could affect the internal behaviour of RSpec or Ruby.
Another option is to call it explicitly as Kernel.system('which git').
@wscourge Please reopen if you feel that something needs to be fixed, or even better submit a pull request - we'll be happy to help along your way.
I'm closing this for now.
Hi there, sorry for my absence.
I like your solution with Kernel.system('which git') and am going to use it from now on.
According to the allow_any_instance_of proposition, I'd like to point out for people stumbling upon this issue another rule that tells us not to do so :stuck_out_tongue: which is RSpec/AnyInstance
This issue can remain closed, thanks for your thorough help and keep up the good work! :+1:
@wscourge Please accept my apologies for initially misleading proposal, Kernel.system doesn't seem to be the best fit for you, since it outputs to stdout, and returns process exit code, true.
The following should work better for you:
class A
def x
Kernel.`('which git')
end
end
RSpec.describe A do
it 'returns git binary path' do
allow(Kernel).to receive(:`).with('which git').and_return('/usr/bin/git')
expect(A.new.x).to eq '/usr/bin/git'
end
end
If you comment the allow row out, you'll get:
1) is expected to eq "/usr/bin/git"
Failure/Error: expect(A.new.x).to eq '/usr/bin/git'
expected: "/usr/bin/git"
got: "/usr/local/bin/git\n"
(compared using ==)
Diff:
@@ -1,2 +1,2 @@
-/usr/bin/git
+/usr/local/bin/git
Most helpful comment
One approach would be to wrap the system calls in your own class or module, e.g.:
which you can then easily stub:
I would avoid globally stubbing methods on
ObjectorKernelsince it could affect the internal behaviour of RSpec or Ruby.