ruby-on-railsrubyrspecmodule

How do I call a module method in rspec, with the current module set up


So I have the following module for handling the fetching of and saving of tweets. This is all done on a thread, I am interested in testing two methods with in this module: fetch_tweets and save_tweets

For fetch_tweets, I want to validate that the save_tweets method is called. I don't care what it returns.

For the save_tweets, I want to test that a tweet is actually saved.

So with that in mind, lets look at some code.

Twitter::TwitterHandler - This lives in lib/twitter/twitter_handler.rb

module Twitter
  module TwitterHandler

    def handle_tweets(twitter_client_object)
      Thread.new do
        begin
          twitter_lock(twitter_client_object)
        ensure
          ActiveRecord::Base.clear_active_connections!
        end
      end
    end

    def twitter_lock(twitter_client_object)
      TwitterTweet.with_advisory_lock('aisis-writer-tweets', 0) do
        fetch_tweets(twitter_client_object)
      end
    end

    def fetch_tweets(twitter_client_object)
      Rails.cache.fetch([:aisis_twitter_feed, Time.now.to_i/60/5], expires_in: 1.minute){
        twitter_client.user_timeline(user_id: '252157965', count: 100).map(&:as_json).select{ |h| h["text"].match(/aisiswriter/i) }.take(10).each do |tweet|
          save_tweets(tweet['id_str'], tweet['text'], tweet['created_at'])
        end
      }
    end

    def save_tweets(id, text, created_at)
      tweet = TwitterTweet.find_or_initialize_by(id: id)
      tweet.text = text
      tweet.created_at = created_at
      tweet.save
    end

  end
end

twitter_handler_spec.rb - This lives in spec/twitter/twitter_handler_spec.rb

require 'rails_helper'
require_relative '../../lib/twitter/twitter_handler'

describe 'TwitterHandler' do

  context "fetch tweets method" do
    it "should fetch some tweets" do
      t = Time.parse("01/01/2010 10:00")
      Time.should_receive(:now).and_return(t)
      expect(fetch_tweets(twitter_client)).to receive(:save_tweets).with('342', 'Something', t).and_return nil
    end
  end
end

When I run bin/rspec spec/twitter/twitter_handler_spec.rb I get:

Failures:

  1) TwitterHandler fetch tweets method should fetch some tweets
     Failure/Error: expect(fetch_tweets(twitter_client)).to receive(:save_tweets).with('342', 'Something', t).and_return nil
     NoMethodError:
       undefined method `fetch_tweets' for #<RSpec::ExampleGroups::TwitterHandler::FetchTweetsMethod:0x007ff1c724e300>
     # ./spec/twitter/twitter_handler_spec.rb:11:in `block (3 levels) in <top (required)>'

How do I fix this, so I can call this method and any other method in this module?


Solution

  • You can't call instance methods of a module. You must include or extend the module and then call the methods on the object which included or extended the module. The error you are getting from rspec is because you're calling fetch_tweets from within your example, but fetch_tweets is not defined within the example.

    Once you solve the problem of invoking fetch_tweets, then you've got to deal with the fact that your expect(fetch_tweets(...)) expectation is saying that the object returned by fetch_tweets is going to receive a method call of :save_tweets, which is not what you want. Per your description, you want to set an expectation that whatever object included or extended TwitterHandler will receive save_tweets and then you want to call fetch_tweets.

    If this isn't clear, I suggest you read up on Ruby module methods and then read up on RSpec.