Search code examples
ruby-on-railsrubyruby-on-rails-3rspectdd

How to mock out calls to open-uri


I've got a mailer that uses 'open-uri'.

require 'open-uri'
class NotificationMailer < ActionMailer::Base

  def welcome(picasa_picture)
    picture = picasa_picture.content.src
    filename = picture.split('/').last
    attachments.inline[filename] = open(picture).read
    mail(
      to: '[email protected]',
      from: '[email protected]',
      subject: 'hi',
    )
  end
end

But when I try and test anything the class, I get this error:

 SocketError:
   getaddrinfo: nodename nor servname provided, or not known

I found this SO post: How to rspec mock open-uri and thought it would help. I gave this a try:

let(:pic_content) { double(:pic_content, src: 'http://www.picasa/asdf/asdf.jpeg') }
let(:picture) { double(:picture, content: pic_content) }
let(:open_uri_mock) { double(:uri_mock, read: true) }

subject { described_class.welcome(picture) }

it 'renders email address of sender' do
  subject.stub(:open).and_return(open_uri_mock)
  subject.from.should == [ sender_address ]
end

I also tried a 'should_receive' instead of 'stub', but it didn't help.

How do I suppress the open-uri 'open' method so that it (1) doesn't try to go out to internet and (2) doesn't break my tests?


Solution

  • Why not refactor:

    require 'open-uri'
    class NotificationMailer < ActionMailer::Base
    
      def welcome(picasa_picture)
        picture = picasa_picture.content.src
        filename = picture.split('/').last
        attachments.inline[filename] = open_and_read(picture)
        mail(
          to: '[email protected]',
          from: '[email protected]',
         subject: 'hi',
        )
      end
    
      def open_and_read(picture)
        open(picture).read
      end
    
    end
    

    Then you can stub and test:

    subject { NotificationMailer }
    
    before do 
      subject.stub(:open_and_read).and_return(:whatever_double_you_want)
      subject.welcome(picture)
    end
    
    it 'renders email address of sender' do
      subject.from.should == [ sender_address ]
    end