How to test a Ruby Roda app using RSpec to pass an argument to app.new with initialize

402 Views Asked by At

This question probably has a simple answer but I can't find any examples for using Roda with RSpec3, so it is difficult to troubleshoot.

I am using Marston and Dees "Effective Testing w/ RSpec3" book which uses Sinatra instead of Roda. I am having difficulty passing an object to API.new, and, from the book, this is what works with Sinatra but fails with a "wrong number of arguments" error when I substitute Roda.

Depending on whether I pass arguments with super or no arguments with super(), the error switches to indicate that the failure occurs either at the initialize method or in the call to Rack::Test::Methods post in the spec.

I see that in Rack::Test, in the Github repo README, I may have to use Rack::Builder.parse_file("config.ru") but that didn't help.

Here are the two errors that rspec shows when using super without brackets:

Failures:

  1) MbrTrak::API POST /users when the user is successfully recorded returns the user id
     Failure/Error: post '/users', JSON.generate(user)
     
     ArgumentError:
       wrong number of arguments (given 1, expected 0)
     # ./spec/unit/app/api_spec.rb:21:in `block (4 levels) in <module:MbrTrak>'

And when using super():

  1) MbrTrak::API POST /users when the user is successfully recorded returns the user id
     Failure/Error: super()
     
     ArgumentError:
       wrong number of arguments (given 0, expected 1)
     # ./app/api.rb:8:in `initialize'
     # ./spec/unit/app/api_spec.rb:10:in `new'
     # ./spec/unit/app/api_spec.rb:10:in `app'
     # ./spec/unit/app/api_spec.rb:21:in `block (4 levels) in <module:MbrTrak>'

This is my api_spec.rb:

require_relative '../../../app/api'
require 'rack/test'

module MbrTrak
  RecordResult = Struct.new(:success?, :expense_id, :error_message)
  
  RSpec.describe API do
    include Rack::Test::Methods
    def app
      API.new(directory: directory)
    end
    let(:directory) { instance_double('MbrTrak::Directory')}
    
    describe 'POST /users' do
      context 'when the user is successfully recorded' do
        it 'returns the user id' do
          user = { 'some' => 'user' }
          allow(directory).to receive(:record)
            .with(user)
            .and_return(RecordResult.new(true, 417, nil))
          post '/users', JSON.generate(user)
          parsed = JSON.parse(last_response.body)
          expect(parsed).to include('user_id' => 417)
        end
      end
    end
  end
end

And here is my api.rb file:

require 'roda'
require 'json'

module MbrTrak
  class API < Roda
    def initialize(directory: Directory.new)
      @directory = directory
      super()
    end
    plugin :render, escape: true
    plugin :json
    route do |r|
      r.on "users" do
        r.is Integer do |id|
          r.get do
            JSON.generate([])
          end
        end
        r.post do
          user = JSON.parse(request.body.read)
          result = @directory.record(user)
          JSON.generate('user_id' => result.user_id)
        end
      end
    end
  end
end

My config.ru is:

require "./app/api"

run MbrTrak::API
1

There are 1 best solutions below

0
On

Well roda has defined initialize method that receives env as an argument which is being called by the app method of the class. Looks atm like this

def self.app
   ...
   lambda{|env| new(env)._roda_handle_main_route}
   ...
end

And the constructor of the app looks like this

def initialize(env)

When you run your config.ru with run MbrTrack::API you are actually invoking the call method of the roda class which looks like this

def self.call(env)
  app.call(env)
end 

Because you have redefined the constructor to accept hash positional argument this no longer works and it throws the error you are receiving

ArgumentError:
       wrong number of arguments (given 0, expected 1)

Now what problem are you trying to solve, if you want to make your API class configurable one way to go is to try out dry-configurable which is part of the great dry-ruby gem collection.

If you want to do something else feel free to ask.

It has been a long time since you posted your question so hope you will still find this helpful.