readonly
readonly

Reputation: 356044

RSpec: stubbing Kernel::sleep?

Is there a way to stub Kernel.sleep in an rspec scenario?

Upvotes: 47

Views: 21642

Answers (9)

Sheldon
Sheldon

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

Mike
Mike

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

abhijeetps
abhijeetps

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

Benjamin Bouchet
Benjamin Bouchet

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

Automatico
Automatico

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

Tony Pitluga
Tony Pitluga

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

tig
tig

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

nitecoder
nitecoder

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

James A. Rosen
James A. Rosen

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

Related Questions