0

Here is my rack application:

class MainAppLogic
    def initialize
        Rack::Server.start(:app =>Server, :server => "WEBrick", :Port => "8080")
    end
end

class Server
    def self.call(env)
        return [200, {},["Hello, World"]]
    end
end

When actually run, it behaves as it should and returns "Hello World" to all requests. I'm having trouble convincing rack-test to work with it. Here are my tests:

require "rspec"
require "rack/test"
require "app"

# Rspec config source: https://github.com/shiroyasha/sinatra_rspec    
RSpec.configure do |config|
    config.include Rack::Test::Methods
end

describe MainAppLogic do

    # App method source: https://github.com/shiroyasha/sinatra_rspec
    def app
        MainAppLogic.new
    end

    it "starts a server when initialized" do
        get "/", {}, "SERVER_PORT" => "8080"
        last_response.body.should be != nil
    end
end

When I test this, it fails complaining that MainAppLogic is not a rack server, specifically, that it doesn't respond to MainAppLogic.call. How can I let it know to ignore that MainAppLogic isn't a rack server and just place a request to localhost:8080, because there server has started?

thesecretmaster
  • 1,950
  • 1
  • 27
  • 39
  • This advice doesn't help you with your problem, but you might want to know that standard Ruby indentation is two spaces. It's not bad to indent four--Ruby itself doesn't care--it just looks a little odd. – Wayne Conrad Aug 18 '16 at 13:11

2 Answers2

1

First thing: why the custom class to run the app? You can use the rackup tool, which is the de-facto standard for running Rack apps. Some more details on it here.

Your app code then becomes:

class App
  def call(env)
    return [200, {}, ['Hello, World!']]
  end
end

and with the config.ru

require_relative 'app'

run App.new

you can start the app by running rackup in your project's directory.

As for the error, the message is pretty clear. rack-test expects, that the return value of app method would be an instance of a rack app (an object that responds to call method). Take a look what happens in rack-test internals (it's pretty easy to follow, as a tip—focus on these in given order: lib/rack/test/methods.rb#L30 lib/rack/mock_session.rb#L7 lib/rack/test.rb#L244 lib/rack/mock_session.rb#L30. Notice how the Rack::MockSession is instantiated, how it is used when processing requests (e.g. when you call get method in your tests) and finally how the call method on your app is executed.

I hope that now it's clear why the test should look more like this (yes, you don't need to have a server running when executing your tests):

describe App do
  def app
    App.new
  end

  it "does a triple backflip" do
    get "/"
    expect(last_response.body).to eq("Hello, World")
  end
end

P.S. Sorry for the form of links to rack-test, can't add more than 2 with my current points :P

MrTuna
  • 61
  • 1
  • 4
  • Thanks so much for the answer! I thought it was useless until I refactored my whole project, and now this was my go to reference to figure it out. I see that you're fairly new to Stack Overflow so welcome! Keep writing great answers! – thesecretmaster Aug 23 '16 at 21:51
  • You're welcome! And I've been around for quite a while, just not writing answers ;) – MrTuna Aug 25 '16 at 06:37
0

Your app should be the class name, for example instead of:

def app
  MainAppLogic.new
end

You have to use

def app
  MainAppLogic
end

You shouldn't need to indicate the port for doing the get, because the rack app runs in the context of the tests; so this should be right way:

it "starts a server when initialized" do
    get "/"
    last_response.body.should be != nil
end

Also, as a recommendation prefer to use the new expect format instead of the should, see http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/

And your MainAppLogic, should be something like:

class MainAppLogic < Sinatra::Base
  get '/' do
    'Hello world'
  end
end
Mario Carrion
  • 671
  • 5
  • 11