Kuri
Kuri

Reputation: 25

RSpec How to test when a condition is not met, methods for test verification

"When a customer turns off notifications, notify_on becomes false, and certain conditions are not met. The approach I'm considering is to check whether BellNotify.create! is executed when notify_on is true.

If this approach is correct, please advise how to write the code. I've tried the following:


RSpec.describe NotificationSetting, type: :model do
  describe '#create_notify' do
    context 'when notify_on is true' do
      it 'executes BellNotify.create!' do
        # Set up a notification setting where notify_on is true
        setting = NotificationSetting.new(notify_on: true)
        
        # Allow BellNotify.create! to be called
        allow(BellNotify).to receive(:create!)
        
        # Trigger the method that should call BellNotify.create!
        setting.send(:create_notify)
        
        # Check that BellNotify.create! has been received
        expect(BellNotify).to have_received(:create!)
      end
    end
  end
end

If there is another approach to test please let me know

I attempted to verify whether a method of a certain class is executed or not when specific conditions are not met.

Upvotes: 0

Views: 62

Answers (2)

aridlehoover
aridlehoover

Reputation: 3585

In Ruby, predicate methods (those methods that return true or false) always end in a question mark, like this: authorized?. You are essentially asking the object a yes/no question.

And, event handlers usually start with on_ as in on_create.

From the context in the test, I believe you're using notify_on as an on/off switch. I think this because you're passing true or false into the initializer. It would be more clear if you were to name the parameter notify and use a private method named notify? to return true or false.

It would also help to name your nouns (models) with nouns (like BellNotification), not verbs (like BellNotify).

Here's what canonical Ruby might look like in a class that passes your test...

class NotificationSettings
  def initialize(notify: true)
    @notify = notify
  end

  def create_notification
    # do something

    BellNotification.create! if notify?
  end

  private

  def notify?
    @notify == true
  end
end

Then, to test this, @RobertoCarillo had it right. Here it is written without the let or before statements. Admittedly, this is a preference. I prefer putting the setup inside the it block because it makes the test easy to understand without having to scroll up and down in a file with hundreds (or thousands) of lines of code...

RSpec.descripe NotificationSettings do
  describe '#create_notification' do
    it 'creates a BellNotification when notify? is true' do
      # Arrange - setup the test
      settings = NotificationSettings.new(notify: true)
      allow(BellNotification).to receive(:create!)

      # Act - perform the action under test (with the side effect)
      settings.create_notification

      # Assert - that the correct result happened
      expect(BellNotification).to have_received(:create!)
    end

    it 'does NOT create a BellNotification when notify? is false' do
      # Arrange - setup the test
      settings = NotificationSettings.new(notify: false)
      allow(BellNotification).to receive(:create!)

      # Act - perform the action under test (with the side effect)
      settings.create_notification

      # Assert - that the correct result happened
      expect(BellNotification).not_to have_received(:create!)
    end
  end
end

Upvotes: 0

RobertoCarrillo
RobertoCarrillo

Reputation: 39

I think the approach is correct, I would just make some adjustments to avoid repeating a couple of lines.

RSpec.describe NotificationSetting, type: :model do
  describe '#create_notify' do
    let(:notify_on) { true }
    let(:settings) { NotificationSetting.new(notify_on: notify_on) }
  
    before { allow(BellNotify).to receive(:create!) }

    context 'when notify_on is true' do
      it 'executes BellNotify.create!' do
        setting.send(:create_notify)
        expect(BellNotify).to have_received(:create!)
      end
    end

    context 'when notify_on is false' do
      let(:notify_on) { false }

      it 'does not execute BellNotify.create!' do
        setting.send(:create_notify)
        expect(BellNotify).not_to have_received(:create!)
      end
    end
  end
end

Upvotes: 0

Related Questions