Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to assert that a method call was not made, without any_instance?

I have a class, that in one situation should call :my_method, but in another situation must not call method :my_method. I would like to test both cases. Also, I would like the test to document the cases when :my_method should not be called.

Using any_instance is generally discouraged, so I would be happy to learn a nice way to replace it.

This code snippet is a reduced example on what I kind of test I would like to write.

class TestSubject
  def call
    call_me
  end

   def call_me; end
   def never_mind; end
end

require 'rspec'

spec = RSpec.describe 'TestSubject' do
  describe '#call' do
    it 'calls #call_me' do
      expect_any_instance_of(TestSubject).to receive(:call_me)
      TestSubject.new.call
    end

    it 'does not call #never_mind' do
      expect_any_instance_of(TestSubject).not_to receive(:never_mind)
      TestSubject.new.call
    end
  end
end

spec.run # => true

It works, but uses expect_any_instance_of method, which is not recommended.

How to replace it?

like image 489
Matilda Smeds Avatar asked Oct 19 '18 09:10

Matilda Smeds


People also ask

How to check if a method is being called or not?

Validation methods throwing exceptions are usually the easiest to check for being called. All you need to do is feed the calling method invalid data and check that the exception is actually thrown. Using MSTest you would need to use the ExpectedException attribute, or code a try catch yourself.

What is assert_called_once_with method?

assert_called_with() This method is a convenient way of asserting that calls are made in a particular way: assert_called_once_with() Assert that the mock was called exactly once and with the specified arguments. assert_any_call() assert the mock has been called with the specified arguments.

What is the difference between assert and assert_has_calls?

assert the mock has been called with the specified arguments. The assert passes if the mock has ever been called, unlike assert_called_with() and assert_called_once_with() that only pass if the call is the most recent one. assert_has_calls() assert the mock has been called with the specified calls.

How to assert the Mock has been called with specified calls?

assert the mock has been called with the specified calls. The mock_calls list is checked for the calls. If any_order is false (the default) then the calls must be sequential. There can be extra calls before or after the specified calls. If any_order is true then the calls can be in any order, but they must all appear in mock_calls.


2 Answers

I'll do somehting like that

describe TestSubject do
  describe '#call' do
    it 'does not call #something' do 
      subject = TestSubject.new
      allow(subject).to receive(:something)

      subject.call

      expect(subject).not_to have_received(:something)
    end
  end
end

Hope this helped !

like image 90
kevcha Avatar answered Oct 20 '22 06:10

kevcha


This is how I normally unit-test. I updated the code to support other possible questions you (or other readers) may have in the future.

class TestSubject
  def call
    some_call_me_value = call_me
    call_you(some_call_me_value)
  end

  def call_me; end
  def call_you(x); end
  def never_mind; end

  class << self
    def some_class_method_a; end

    def some_class_method_b(x, y); end
  end
end

require 'rspec'

spec = RSpec.describe TestSubject do
  context 'instance methods' do
    let(:test_subject) { TestSubject.new }

    describe '#call' do
      let(:args) { nil }
      let(:mocked_call_me_return_value) { 'somecallmevalue' }
      subject { test_subject.call(*args) }

      before do
        allow(test_subject).to receive(:call_me) do
          mocked_call_me_return_value
        end
      end

      it 'calls #call_me' do
        expect(test_subject).to receive(:call_me).once
        subject
      end

      it 'calls #call_you with call_me value as the argument' do
        expect(test_subject).to receive(:call_you).once.with(mocked_call_me_return_value)
        subject
      end

      it 'does not call #never_mind' do
        expect(test_subject).to_not receive(:never_mind)
        subject
      end

      it 'calls in order' do
        expect(test_subject).to receive(:call_me).once.ordered
        expect(test_subject).to receive(:call_you).once.ordered
        subject
      end
    end

    describe '#call_me' do
      let(:args) { nil }
      subject { test_subject.call_me(*args) }

      # it ...
    end

    describe '#call_you' do
      let(:args) { nil }
      subject { test_subject.call_you(*args) }

      shared_examples_for 'shared #call_you behaviours' do
        it 'calls your phone number'
        it 'creates a Conversation record'
      end

      # just an example of argument-dependent behaviour spec
      context 'when argument is true' do 
        let(:args) { [true] }

        it 'does something magical'
        it_behaves_like 'shared #call_you behaviours'
      end

      # just an example of argument-dependent behaviour spec
      context 'when argument is false' do
        let(:args) { [false] }

        it 'does something explosive'
        it_behaves_like 'shared #call_you behaviours'
      end
    end
  end

  context 'class methods' do
    let(:args) { nil }

    describe '#some_class_method_a' do
      let(:args) { nil }
      subject { TestSubject.some_class_method_a(*args) }

      # it ...
    end

    describe '#some_class_method_b' do
      let(:args) { [1, 2] }
      subject { TestSubject.some_class_method_b(*args) }

      # it ...
    end
  end
end

spec.run # => true
like image 26
Jay-Ar Polidario Avatar answered Oct 20 '22 04:10

Jay-Ar Polidario