Reputation: 356044
Is there a way to stub Kernel.sleep in an rspec scenario?
Upvotes: 47
Views: 21642
Reputation: 166
Since Kernel
is an ancestor of all Objects, one should be able to mock the inherited sleep
method on the class under test.
MyClass.ancestors --> [MyClass, Object, PP::ObjectMixin, Kernel, BasicObject]
The mock can be accomplished with code similar to this.
before do
allow(described_class).to receive(:sleep).and_return('')
end
The following more detailed example shows mocking sleep
for when it is called within class and instance methods. Would enjoy hearing comments from more experienced Ruby developers.
Class:
class MyClass
class << self
def klass_action(sleep_seconds)
sleep(sleep_seconds)
end
end
def self.class_action(sleep_seconds)
sleep(sleep_seconds)
end
def do_some_action(sleep_seconds)
sleep(sleep_seconds)
end
end
Tests:
describe MyClass, focus: true do
subject { MyClass.new }
let(:sleep_seconds) { 12 }
context 'sleep called from an instance' do
before do
allow(subject).to receive(:sleep).and_return('')
end
it 'sleeps for the requested number of seconds' do
subject.do_some_action(sleep_seconds)
expect(subject).to have_received(:sleep).with(sleep_seconds)
end
end
context 'sleep called from a class' do
before do
allow(described_class).to receive(:sleep).and_return('')
end
it 'class method sleeps for the requested number of seconds' do
described_class.class_action(sleep_seconds)
expect(described_class).to have_received(:sleep).with(sleep_seconds)
end
it 'klass method sleeps for the requested number of seconds' do
described_class.klass_action(sleep_seconds)
expect(described_class).to have_received(:sleep).with(sleep_seconds)
end
end
end
Upvotes: 0
Reputation: 87
For rspec version 1, the stubbing syntax has changed. This works:
allow_any_instance_of(Kernel).to receive(:sleep).and_return("")
When I use it.
Upvotes: 2
Reputation: 5700
Here's the newer way of stubbing Rspec with Kernal::Sleep
.
This is basically an update to the following answer: Tony Pitluga's answer to the same question
class Foo
def self.some_method
sleep 5
end
end
it "should call sleep" do
allow_any_instance_of(Foo).to receive(:sleep)
expect(Foo).to receive(:sleep).with(5)
Foo.some_method
end
Upvotes: 3
Reputation: 13181
When the call to sleep
is not within an object (while testing a rake task for example), you can add the following in a before block (rspec 3 syntax)
allow_any_instance_of(Object).to receive(:sleep)
Upvotes: 31
Reputation: 12936
I was not able to get the other solutions here to work. Maybe something have changed in the way sleep is handled in newer versions of Ruby, or something else.
What I ended up doing was to monkey-patch the Object class as it appears that this is what receives the sleep calls. So I simply added this:
class Object
def sleep(*args)
end
end
So the sleep method now does nothing in stead of something. There might be some way of mocking this better, but I was not able to find a good solution without mocking the sleep metohd
of every single object that potentially used it.
Upvotes: 0
Reputation:
If you are calling sleep within the context of an object, you should stub it on the object, like so:
class Foo
def self.some_method
sleep 5
end
end
it "should call sleep" do
Foo.stub!(:sleep)
Foo.should_receive(:sleep).with(5)
Foo.some_method
end
The key is, to stub sleep on whatever "self" is in the context where sleep is called.
Upvotes: 52
Reputation: 27850
I needed to stub require and after long searching I found that the only way that worked for me is this
def method_using_sleep
sleep
sleep 0.01
end
it "should use sleep" do
@expectations = mock('expectations')
@expectations.should_receive(:sleep).ordered.with()
@expectations.should_receive(:sleep).ordered.with(0.01)
def sleep(*args)
@expectations.sleep(*args)
end
method_using_sleep
end
Upvotes: 0
Reputation: 5486
In pure rspec:
before do
Kernel.stub!(:sleep)
end
it "should sleep" do
Kernel.should_receive(:sleep).with(100)
Object.method_to_test #We need to call our method to see that it is called
end
Upvotes: 5
Reputation: 65281
If you're using Mocha, then something like this will work:
def setup
Kernel.stubs(:sleep)
end
def test_my_sleepy_method
my_object.take_cat_nap!
Kernel.assert_received(:sleep).with(1800) #should take a half-hour paower-nap
end
Or if you're using rr:
def setup
stub(Kernel).sleep
end
def test_my_sleepy_method
my_object.take_cat_nap!
assert_received(Kernel) { |k| k.sleep(1800) }
end
You probably shouldn't be testing more complex threading issues with unit tests. On integration tests, however, use the real Kernel.sleep
, which will help you ferret out complex threading issues.
Upvotes: 4