How to test, in rspec, environment determined with Settings.production? - ruby

I have logic in a Sinatra project that determines different behaviour depending on if the environment is production or development.
if Services.production?
# do something
else
# do something else
end
How can I test this code? I have tried the following but it didn't work:
expect_any_instance_of(Services).to receive(:production?).and_return(true)

It's not an instance of Services you're calling production? on, it's the Services class itself. You should be able to just do
expect(Services).to receive(:production?).and_return(true)

From your code it looks like production? is a class method, so it's not being called on an instance of Services, but rather on the class Services.
Try
expect(Services).to receive(:production?).and_return(true)

Related

Accessing Sinatra application scope from Rake task

I have a global variable in a Sinatra application that I want to update with a scheduled task from my Rakefile. Note that the application is hosted on Heroku. I have set up helpers in order to access this variable.
get '/' do
##var
end
helpers do
def get_var
return ##var
end
def set_var(value)
##var = value
end
end
Here is the task in my Rakefile:
task :do_something do
Sinatra::Application.set_var(get_data)
end
def get_data
# Retrieve value from external source
...
return val
end
The problem I run into is that the task executes properly, but the variable in the Sinatra application is never updated. I assume this is because calling Sinatra::Application from within the Rakefile actually creates a separate instance of the application from the primary instance that I am trying to update.
I want to know if their is a way to access the scope of the running Sinatra web app from within a Rakefile task.
*Note: I could just write the value retrieved in the scheduled task to a database and then access it from the Sinatra app, but that would be overkill because this variable is updated so infrequently but retrieved so regularly that I would prefer to keep it in memory for easier access. I have looked into Memcache and Redis in order to avoid turning to a database, but again I feel that this would be excessive for a single value. Feel free to disagree with this.
EDIT: In regards to Alexey Sukhoviy's comment, Heroku does not allow writing to files outside of the tmp directories, and these are not kept alive long enough to satisfy the needs of the application.
I ended up storing the variable using Memcache. This plays well with Heroku since they provide a free add-on for it. The Dalli gem provides a simple interface with Ruby for Memcache. In my Sinatra app file, I set the following options:
require 'dalli'
set :cache, Dalli::Client.new
I then am able to recover the stored variable from the Rakefile:
task :do_something do
Sinatra::Application.settings.cache.set('var', get_data)
end
I can then access this variable again in my Sinatra controller:
settings.cache.get('var')

How do I test methods in a Sinatra app?

How do I access the app instance so I can test the foobar() method?
class App < Sinatra::Base
get '/' do
return foobar
end
def foobar
"hello world"
end
end
This is a late reply, but I am trying to find a flexible way to do that too.
I found in Sinatra's source code (1.4.5) that creating an instance of the app with new! allows testing the app's methods directly. Exert from a test setup with Test::Unit and Shoulda.
class AppTest < Test::Unit::TestCase
setup do
#app = App.new! # here is the point.
end
should 'say hello to the world' do
assert_equal "hello world", #app.foobar # or #app.send(:foobar) for private methods.
end
end
There are consequences. Using new! does not create the usual Sinatra::Wrapper that becomes the entry point to the middleware pipeline and the app in normal settings. So the new! approach will work only if the tested methods are really "helpers" that do not rely on middleware functionalities (e.g. SSL).
Alternatively, a post on Rspec proposes an alternative solution. I used something similar in the past, but it requires more work that is not always the best choice. It had the advantage to offer broader coverage of code to test. The isolation of the app with new! sounds good though, if we are taking about "unit" testing.
Note on #three's comment: A non-trivial app should separate API methods (usually in the app) from all helpers, etc. Helpers ending up in a separate file are cleaner, easier to maintain, and easier to test. But I definitely understand cases where a first version of an app would include a few helpers, with awareness that refactoring will be necessary. And even then having tests brings in some more confidence in the software itself, and in the future refactoring as well.
It doesn't matter what you test - it is how :) => http://www.sinatrarb.com/testing.html

How do I set a global variable in Sinatra depending on the production or development environment?

I'm using Sinatra's set method in order to assign a global variable:
set :location, 'Melbourne'
I want to update this so that the variable is static or dynamic depending on whether the app is in development or production. I tried this below, which works in development, but not in production:
set :location, production? ? request.location.city : 'Melbourne'
The request.location.city is from the geolocation gem, and this method work fine in production in other situations. Is there something in the if statement that I'm missing, or does the Sinatra set method not accept statements?
Request is not available at the top level, only inside request handlers.
Write a method instead of a global setting, e.g.:
def location(request)
production? ? request.location.city : 'Melbourne'
end

Is there a Rack or Sinatra based environment configuration utility?

Is there anything in the Sinatra / Rack world similar to Rails configuration loading scheme that loads one of the config\enviroments\*.rb files depending on Rails.env
I know I could develop one pretty easily, i was just wondering if there was something already in place.
If you're following the Rails convention of putting a file for each environment in config/environments/environment_name.rb, you can put something like this in your Sinatra app, or for Rack in your config.ru file:
Dir.glob(File.dirname(__FILE__) + "/config/environments/#{settings.environment}.rb", &method(:require))
With some minor modifications you could make it load other file locations/combinations. Sinatra's configure blocks work just as well, too.
It turns out that there is something from Sinatra, that provides a similar, though limited, functionality.
See the code:
https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1120
So that you can do this:
class MyApp < Sinatra::Base
configure :development, :test do
#only executes this code when environment is equal to one of the passed arguments
# I'm pretty sure Sinatra sets this based on ENV['RACK_ENV']
end
end
There is one called Sinatra::ConfigFile, which now lives in Sinatra::Contrib http://www.sinatrarb.com/contrib/config_file.html
There's lots of useful stuff in there.
I adapted mine from monkrb.com (it's also yaml in RoR anyways)
YAML.load_file(path_of "config/settings.yml")[RACK_ENV]
e.g.
http://github.com/codepants/yasumi/blob/master/config/settings.yml

Stubbing a controller method with Sinatra and rspec

So I'm trying to figure out a way of stubbing a controller method in rspec for a Sinatra app. The main reason for this is to test the logical flow of the application and to make sure it calls the necessary functions when certain conditions are met. So, in essence, I want to be able to do something like
controller.should_receive(:fancy_method).and_return("This is a string")
What I'm having difficulty doing is accessing the controller instance within the sinatra app. I am able to override the current functions using a class_eval on the sinatra controller class, but I'd love to assert that these functions actually run.
Anyone have any advice?
Thanks.
Dan, I believe what you really want is to just test the controller actions. From a tester's perspective you shouldn't really care about what it actually called but rather for the output, given a specific input and maybe some other special conditions (that is mocking or stubbing other classes) (1).
You can check the official documentation for Sinatra + Rack::Test or this blog post from devver.net.
(1) : If your controller pages are calling some other classes (models, services, etc) you could mock these instead and put expectations on them. For example :
SomeClass.should_receive(:msg).with(:arg).and_return(:special_value)
Some more info for mocking (with RSpec in this exmaple) can be found on the RSpec documentation pages.

Resources