How do I test methods in a Sinatra app? - ruby

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

Related

Annoying Guard notification when testing

Recently I made a simple ruby application and have been using minitest to test it.
Following the advice of the Head First Ruby book, I automated this testing using Rake(I'll write what it told me to put in the Rakefile at the end of this post, in case that helps). The test seems to run fine (everything passes in a way I would expect it to), but I always get this notification at the end of it all:
rvm/gems/ruby-2.3.0/gems/guard-2.14.0/lib/guard/notifier.rb:28: warning: instance variable #notifier not initialized
Testing things manually by telling ruby to include which files I want, does not have this issue, only when I use "rake test" to test things.
As far as I can tell, this is related to when I set up Guard when I was following Michael Hartl's Rails Tutorial, at the end of chapter 3. I followed the directions for setting that up (correctly, as far as I can tell), and this was all in a completely different folder(ultimately my ruby and rails projects do have the same parent folder that they sit in, but are themselves in completely separate ruby_projects and rails_projects folders). If possible, I would like to stop this notification on my ruby application that I am testing. Is there a good way to do this?
Contents of the Rakefile I am using, if that helps:
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "lib"
t.test_files=FileList['test/**/test_*.rb']
end
My test file requires minitest/autorun, and the file for the application that I am testing, then has the normal tests
Seems like there's some weird conflict...
The reason is that Guard::Notifier.connect isn't connected. Normally, when you run guard, Guard.setup is called which does this.
If you're not using guard (e.g. interactively), then calling the following from your Rakefile should work around the problem:
Guard::Notifier.connect(notify: false, silent: true)
Guard::Notifier.disconnect
This will initialize the variable.
For a faster response, always report such issues on the project page on Github. If you can share the project where this occurs, maybe a better fix is possible. (It's best to provide a repository, since it really speeds up fixing things and often errors like this are very hard to simulate without the exact code).

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

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)

Rails mount engine while allowing to subclass any engine class

I'm building a platform in Rails that will act as a basis for a few other Rails applications. Ideally I would like this to be a shared engine, on which the other concrete applications can build.
It would be nice if the applications could extend the base classes that the engine provides. AFAIK this can be done using monkey patching, but that feels 'hacky' to me.
I stumbled onto what looked like the solution, which was to just create 'mirror' classes in the main Rails app, which extend those of the engine:
# SharedEngine/models/shared_engine/post.rb
module SharedEngine
class Post < ActiveRecord::Base
def hello
"Hello"
end
end
end
# App/models/shared_engine/post.rb
require SharedEngine::Engine.root.join('app', 'models', 'shared_engine', 'post')
class Post < SharedEngine::Post
def hello
super + " world"
end
end
However, it looks like there are some autoloading problems. After server startup, it prints "Hello". Then after I save the app model, it says "Hello world".
The Rails engine guide suggests putting shared code into concerns. Is there any other way to get this working cleanly without having to create concerns for every class?
Since you need to create a basis for a few Rails apps to reduce boilerplate i suggest having a look a Rails Application Templates. Using templates you may provide all the necessary scaffolding without monkey-patching any class.

Why sinatra is a DSL?

The very first line written about sinatra is it is DSL for quickly creating web applications in Ruby with minimal effort. I can understand it is light weight, very flexible, quick for creating web apps and with minimal effort but not able to understand how it is a DSL?
One reason is that it defines actions ("verbs") within its domain as methods, for example:
get '/hi' do
"Hello World!"
end
Here Sinatra has incorporated an action from its domain--namely the HTTP request method "GET"--into its "vocabulary."
(Similar to building a library around banking and defining methods like account or customer.)
Is this more about the true definition of DSL?

Stubbing Sinatra helper in Cucumber

I am currently struggling with stubbing out a helper method of my Sinatra app from within Cucumber.
I have a Sinatra app with simple session authentication (by cookies) and I want to turn of authentication by stubbing out the logged_in? helper method for my Cucumber scenarios.
There seems to be a problem with Sinatra and Cucumber concerning sessions so I thought about just using Mocha to work around the problem.
However I don't know how I can access the Sinatra::Application instance from within a Given-Block to stub out the method.
It seems like I need to directly override my Authentication mechanism within a Before do ... end-block
So I ended up with a hooks.rb placed in features/support/ file overwriting my logged_in? and the current_user method.
Before do
MySinatraApplicationClass.class_eval do
helpers do
def logged_in?
return true
end
def current_user
# This returns a certain Username usually stored
# in the session, returning it like
# that prohibits different user logins, but for
# now this is enough for me
"Walter"
end
end
end
end
The only thing I had to take care of, is that the no other actions within the application directly read from session but rather use those helpers.
Sadly I think this way of handling session based Sinatra applications through Cucumber is already described somewhere else and I just thought my problem was different.
You can get the right context by using Sinatra::Application.class_eval
Edit: See original poster's answer for full explanation.

Resources