stevo
12/3/2017 - 12:50 PM

Testing private methods

Testing private methods

# Poor specs
# ==========

describe CloseOrder do
  # Testing #call using partial double of object being tested 
  # looks unnatural and feels as a duplication
  describe '#call' do
    it do
      order = create(:order)
      service = CloseOrder.new(order)
      allow(service).to receive(:send_notification)
      
      service.call
      
      expect(service).to have_received(:send_notification)
    end
  end
  
  describe '#send_notification' do
    it 'delivers order closed notification to customer' do
      order = create(:order, customer_email: 'tony@stark.com')  
      service = CloseOrder.new(order)
    
      expect { 
        service.send(:send_notification) # We are forced to use #send to test private method
      }.to change { ActionMailer::Base.deliveries.count }.by(1)

      notification = ActionMailer::Base.deliveries.last
      expect(notification).to have_attributes(subject: 'Order closed!', recipients: ['tony@stark.com'])
    end
  end
end

# Good specs
# ==========

describe CloseOrder do
  describe '#call' do
    it 'delivers order closed notification to customer' do
      order = create(:order, customer_email: 'tony@stark.com')  
      service = CloseOrder.new(order)
    
      expect { 
        service.call 
      }.to change { ActionMailer::Base.deliveries.count }.by(1)

      notification = ActionMailer::Base.deliveries.last
      expect(notification).to have_attributes(subject: 'Order closed!', recipients: ['tony@stark.com'])
    end
  end
end

# Implementation
# ==============

class CloseOrder
  def initialize(order)
    @order = order
  end
  
  def call
    send_notification
  end
  
  private
  
  def send_notification
    OrderMailer.order_closed_notification(@order).deliver_now
  end  
end