How to stub a method used in a Sinatra app with Minitest? - sinatra

I want to test a small Sinatra application. And to do so, stub out some behaviour.
The relevant code in the Sinatra app is:
require 'sinatra'
require 'yaml'
get '/commands' do
YAML.load_file("commands.yml")["commands"].join("\n")
end
The minitest/spec code to test that is:
include Rack::Test::Methods
def app
Sinatra::Application
end
describe "/commands" do
describe "with two commands defined" do
it "must list the two commands" do
YAML.stub(:load_file, {"commands" => ["hostname", "uptime"]}) do
get '/commands'
last_response.must_be :ok?
last_response.body.must_equal "hostname\nuptime"
end
end
end
describe "with no commands defined" do
YAML.stub(:load_file, {"commands" => nil}) do
it "must list no commands" do
get '/commands'
last_response.body.must_equal ""
end
end
end
end
Using a small spec_helper:
ENV['RACK_ENV'] = 'test'
require 'minitest/autorun'
require 'minitest/spec'
require 'rack/test'
require_relative '../minion' #This is the sinatra-app.
The YAML.stub part is the most important, as it does not work as expected. It does stub out YAML.load_file inside the spec just fine. But in the context of the sinatra-app, YAML.load_file is unstubbed and loads the commands.yml, despite my attempt to stub it out.
How can I stub out a call to YAML.load_file with minitest/spec and have that stub being used in the Sinatra application?

Related

How to switch from Sqlite to Postgres while installing Warden on Sinatra on Heroku

This is partly a problem-solving question, partly a "I'm trying to understand what's going on" question. I hope that's allowed. Basically, I'm trying to get Warden user authentication to work with Ruby/Sinatra and Postgres on Heroku.
I got a lot of help from this handy (but oldish) tutorial.
From some Rails experience I am a bit familiar with Active Record. The tutorial didn't mention anything about creating a migration for the User class. I went ahead and made my own migration, with my own properties ("name", "email", "password"), only to discover later that, lo and behold, the properties I put in that migration weren't being used by (and in fact were rejected by) the actual model in use. When I examined the object instances in the database, I found that they had only the properties Warden provided for me ("username" and "password").
I'm just trying to understand what happened here. I migrated down my (apparently unnecessary and ignored) Users migration, and nothing happened. I mean that I was able to create User instances and log in using them just as before.
Then it occurred to me that this old Warden tutorial (from 2012) uses something called DataMapper, which does what Active Record would do today. Is that right? They are both "ORMs"? I'm still confused about why Sinatra completely ignored the User migration I did. Maybe it's just using a different database--I did notice wht might be a new db.sqlite database in my main file. Pretty sure the one I created for Active Record was db/madlibs.sqlite3.
Although it works on my local machine, I'm pretty sure it won't work on Heroku, since they don't support sqlite (pretty sure). That then means I'll have to go back to the Warden documentation and figure out how to get it to work with my Postgres database...right? Any pointers on how to get started with that? Since this will be my first project using any authentication library like Warden, it's pretty intimidating.
Here's what I have so far (repo):
app.rb:
require 'sinatra'
require 'sinatra/activerecord'
require 'sinatra/base'
require './config/environment'
require 'bundler'
Bundler.require
require './model'
enable :sessions
class Madlib < ActiveRecord::Base
end
class SinatraWardenExample < Sinatra::Base
register Sinatra::Flash
end
use Warden::Manager do |config|
config.serialize_into_session{|user| user.id }
config.serialize_from_session{|id| User.get(id) }
config.scope_defaults :default,
strategies: [:password],
action: 'auth/unauthenticated'
config.failure_app = self
end
Warden::Manager.before_failure do |env,opts|
env['REQUEST_METHOD'] = 'POST'
end
Warden::Strategies.add(:password) do
def valid?
params['user']['username'] && params['user']['password']
end
def authenticate!
user = User.first(username: params['user']['username'])
if user.nil?
fail!("The username you entered does not exist.")
elsif user.authenticate(params['user']['password'])
success!(user)
else
fail!("Could not log in")
end
end
end
...non authentication routes...
post '/auth/login' do
env['warden'].authenticate!
flash[:success] = env['warden'].message
if session[:return_to].nil?
redirect '/'
else
redirect session[:return_to]
end
end
get '/auth/logout' do
env['warden'].raw_session.inspect
env['warden'].logout
flash[:success] = 'Successfully logged out'
redirect '/'
end
post '/auth/unauthenticated' do
session[:return_to] = env['warden.options'][:attempted_path]
puts env['warden.options'][:attempted_path]
flash[:error] = env['warden'].message || "You must log in"
redirect '/auth/login'
end
get '/protected' do
env['warden'].authenticate!
#current_user = env['warden'].user
erb :protected
end
model.rb (just the User model):
require 'rubygems'
require 'data_mapper'
require 'dm-sqlite-adapter'
require 'bcrypt'
DataMapper.setup(:default, "sqlite://#{Dir.pwd}/db.sqlite")
class User
include DataMapper::Resource
include BCrypt
property :id, Serial, :key => true
property :username, String, :length => 3..50
property :password, BCryptHash
def authenticate(attempted_password)
if self.password == attempted_password
true
else
false
end
end
end
DataMapper.finalize
DataMapper.auto_upgrade!
It seems like this repo might have solved the problems I'm facing now. Should I study that? The Warden documentation itself is pretty forbidding for a relative beginner. For example, it says "Warden must be downstream of some kind of session middleware. It must have a failure application declared, and you should declare which strategies to use by default." I don't understand that. And then it gives some code...which I also don't quite understand. Advice?? (Should I be working with a teacher/mentor, maybe?)

Debugging test cases when they are combination of Robot framework and python selenium

Currently I'm using Eclipse with Nokia/Red plugin which allows me to write robot framework test suites. Support is Python 3.6 and Selenium for it.
My project is called "Automation" and Test suites are in .robot files.
Test suites have test cases which are called "Keywords".
Test Cases
Create New Vehicle
Create new vehicle with next ${registrationno} and ${description}
Navigate to data section
Those "Keywords" are imported from python library and look like:
#keyword("Create new vehicle with next ${registrationno} and ${description}")
def create_new_vehicle_Simple(self,registrationno, description):
headerPage = HeaderPage(TestCaseKeywords.driver)
sideBarPage = headerPage.selectDaten()
basicVehicleCreation = sideBarPage.createNewVehicle()
basicVehicleCreation.setKennzeichen(registrationno)
basicVehicleCreation.setBeschreibung(description)
TestCaseKeywords.carnumber = basicVehicleCreation.save()
The problem is that when I run test cases, in log I only get result of this whole python function, pass or failed. I can't see at which step it failed- is it at first or second step of this function.
Is there any plugin or other solution for this case to be able to see which exact python function pass or fail? (of course, workaround is to use in TC for every function a keyword but that is not what I prefer)
If you need to "step into" a python defined keyword you need to use python debugger together with RED.
This can be done with any python debugger,if you like to have everything in one application, PyDev can be used with RED.
Follow below help document, if you will face any problems leave a comment here.
RED Debug with PyDev
If you are wanting to know which statement in the python-based keyword failed, you simply need to have it throw an appropriate error. Robot won't do this for you, however. From a reporting standpoint, a python based keyword is a black box. You will have to explicitly add logging messages, and return useful errors.
For example, the call to sideBarPage.createNewVehicle() should throw an exception such as "unable to create new vehicle". Likewise, the call to basicVehicleCreation.setKennzeichen(registrationno) should raise an error like "failed to register the vehicle".
If you don't have control over those methods, you can do the error handling from within your keyword:
#keyword("Create new vehicle with next ${registrationno} and ${description}")
def create_new_vehicle_Simple(self,registrationno, description):
headerPage = HeaderPage(TestCaseKeywords.driver)
sideBarPage = headerPage.selectDaten()
try:
basicVehicleCreation = sideBarPage.createNewVehicle()
except:
raise Exception("unable to create new vehicle")
try:
basicVehicleCreation.setKennzeichen(registrationno)
except:
raise exception("unable to register new vehicle")
...

Sinatra Error log integration with sentry

Below are my config.ru file
require 'raven'
require './managers/log_manager.rb'
logger = LogManager.create_logger('/error.log')
logger.log(Logger::ERROR, "********** just testing **********")
puts "#{logger.inspect}"
Raven.configure do |config|
config.dsn = 'https://secrect'
config.logger = logger
config.environments = 'development'
end
use Raven::Rack
Only exceptions got notify. My problem is to get notify for Error log data, but currently it didn't.
Because Ruby doesn't have a consistent logging solution you'll probably have to write your own handler.
If, i.e. the logging helper gives you an Event, you'd probably do something like this:
def my_log_helper(event)
if event.really_is_an_exception
Raven.capture_exception(event.message)
else
Raven.capture_message(event.message)
end
end
p.s. sorry about my awful ruby, I'm not fluent
The main thing is that Raven tries to be magical when it can, but outside of that it tends to explicitness.
There's many other things you can do with integration, such as sending localized context, and things that are generally environment-specific, but the basics are mostly straightforward.

How Does the ActiveResource Get Call show find(:first) or find(:last) requests?

I am developing a Sinatra server that can accept calls from ActiveResource, but can"t determine how to identify Get calls specificying :first or :last.
In Rails 3
User.find(:first) => localhost.com/user.xml
User.find(:last) => localhost.com/user.xml
This works exactly as it should according to the examples in the ActiveResource documentation.
It is clear what path they request (the same one), but it is not clear what happens to the :first or :last elements. I can not find them in the request object on the Sinatra server. Does anyone know what happened to those references?
Thanks for your help.
Code from ActiveResource library
def find(*arguments)
scope = arguments.slice!(0)
options = arguments.slice!(0) || {}
case scope
when :all then find_every(options)
when :first then find_every(options).first
when :last then find_every(options).last
when :one then find_one(options)
else find_single(scope, options)
end
end
last and first just methods from Enumerable module

Testing view helpers

I'm currently working on a Rails plugin used for generating iPhone specific HTML meta-tags. I'm trying to use ActionView::TestCase for unit tests but keep getting the same error. See file contents and error below. Any ideas or help would be much appreciated.
test_helper.rb
require 'rubygems'
require 'test/unit'
require 'active_support'
require 'action_view'
require File.join(File.dirname(__FILE__), '..', 'lib', 'iphone_helper')
iphone_test_helper.rb
require 'test_helper'
class IphoneHelperTest < ActionView::TestCase
test 'br' do
tag = tag('br')
assert_tag_in tag, '<br />'
end
end
error
RuntimeError: In order to use #url_for, you must include routing helpers explicitly. For instance, `include Rails.application.routes.url_helpers
Awful and hacky workaround that worked for me (since I am working on a gem and not in a full rails environment):
require 'ostruct'
module ActionController::UrlFor
def _routes
helpers = OpenStruct.new
helpers.url_helpers = Module.new
helpers
end
end
Did you try to include the respective Module in an old-fashioned way?:
include ActionDispatch::Routing::RouteSet
If a NameError is raised telling you that ActionDispatch is unknown you might have to require 'action_dispatch'.
Maybe a stupid question, but is the fact that the class name and the file name don't match possibly a problem (IphoneHelperTest vs. iphone_test_helper.rb)? Sometimes that leads to classes not being loaded.