Acceptance testing of sinatra app using webrat fails - ruby

I am trying to test a ruby authentication app using minitest and webrat but get errors.
Tests like visit '/' fail with an error Status 200 expected but was 404.
Tests containing code like fill_in :email, :with => "first#company.com" fail with error Could not find field: :email.
I read several sinatra, testing and webrat documents and forums. Some of them were old and suggested stuff like Sinatra::Default, but github.com/brynary/webrat/wiki/sinatra, Building a Sinatra App Driven By Webrat Tests and Learning From the Masters: Sinatra Internals are new, yet they still fail.
Basically, I didn't like sentence-like syntax of rspec, cucumber etc but do want to do behaviour driven development. I really like the minitest syntax, both tests and output and that is why I choose webrat for BDD. If I'm wrong about expecting webrat to fulfill acceptance testing requirements, please simply tell me that I should use this framework or that one.
Apart from that, the first parts of the main file and test file are below. I hope someone can explain me, what I am missing?
test_file
require "test/unit"
require "minitest/autorun"
require "rack/test"
require 'webrat'
require_relative "../lib/kimsin.rb"
Webrat.configure do |config|
config.mode = :rack
end
ENV["RACK_ENV"] = "test"
class KimsinTests < Test::Unit::TestCase
include Rack::Test::Methods
include Webrat::Methods
include Webrat::Matchers
def app
Sinatra::Application.new
end
def test_create_user
visit "/user/new"
fill_in :username, :with => "first#company.com"
fill_in :password, :with => "abC123?*"
fill_in :confirm_password, :with => "abC123?*"
click_link "Register"
assert 201, last_response.status, "Status 201 expected but was #{last_response.status}.\n#{error}"
assert_contain /Logged in as first#company.com./, "No user created"
assert_contain /Logout/, "Logout link not present"
end
main_file
require "sinatra"
require "erb"
require_relative "../lib/kimsin/version"
require_relative "../lib/kimsin/user"
class Kimsin < Sinatra::Application
use Rack::Session::Pool, :expire_after => 2592000
set :session_secret, BCrypt::Engine.generate_salt
configure :development do
DataMapper.auto_migrate!
end
get "/" do
if session[:user_id]
user = User.get session[:user_id]
email = user.email
erb :index, :locals => { :email => email }
else
email = nil
erb :index, :locals => { :email => email }
end
end

Using Sinatra with Webrat should work fine. I think that the errors that you are seeing are caused by the following method (around line 18 in your test file):
def app
Sinatra::Application.new
end
This is setting up the Sinatra::Application base class to run your tests against when you really need to set up your own subclass Kimsin (because you are creating a modular style Sinatra app), i.e.
def app
Kimsin.new
end
The 404 errors and missing fields are happening because Sinatra::Application doesn't define any of the routes you are testing.
You might also like to take a look at Capybara if you are looking for similar alternatives to Webrat.

Related

Unable to use Warden in Sinatra App: env['warden'] returns nil

I'm writing a Sinatra Rack App and I want to use Warden for authentication. I'm using heroku's toolbelt so I use foreman to run my app. I've found some code that's presumably supposed to get this working. Unfortunately, when I attempt to actually access the Warden env object, it is nil.
I've attempted to use the sinatra_warden gem, but it also has its own bugs (might be related to this one).
config.ru:
require './web.rb'
use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"
run MyApp
web.rb:
require 'sinatra'
require 'warden'
require 'data_mapper'
require './config/datamapper.rb'
require './config/warden.rb' # I've tried this inside of MyApp, still didn't work
class MyApp < Sinatra::Base
get '/test' do
env['warden'].authenticate! # env['warden'] is nil :(
end
end
config/warden.rb:
use Rack::Session::Cookie, :secret => ENV['SESSION_SECRET']
use Warden::Manager do |manager|
manager.default_strategies :password
manager.failure_app = MyApp.new
end
Warden::Manager.serialize_into_session { |user| user.id }
Warden::Manager.serialize_from_session { |id| User.get(id) }
Warden::Manager.before_failure do |env,opts|
# Sinatra is very sensitive to the request method
# since authentication could fail on any type of method, we need
# to set it for the failure app so it is routed to the correct block
env['REQUEST_METHOD'] = "POST"
end
Warden::Strategies.add(:password) do
def valid?
params["email"] || params["password"]
end
def authenticate!
u = User.authenticate(params["email"], params["password"])
u.nil? ? fail!("Could not log in") : success!(u)
end
end
Versions:
Sinatra: 1.1.0
Warden: 1.2.1
Rack: 1.4.1
Ruby: 1.9.3p194
Foreman: 0.60.0
Any ideas how to use Warden the set up I've described?
(P.S. Out of curiosity, what exactly is the env variable?)
Rack internally uses the class Rack::Builder to parse your config.ru file and wrap directives to build up the middleware components.
I believe your builder calls to use in config/warden.rb are getting ignored. It may work to remove the directives from that file and add them to the middleware stack in config.ru:
require './web.rb'
use Rack::Session::Cookie, :secret => ENV['SESSION_SECRET']
use Warden::Manager do |manager|
manager.default_strategies :password
manager.failure_app = MyApp.new
end
use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"
run MyApp
Put a link to your config/warden in your config.ru
require File.dirname(__FILE__) + '/config/warden'
Read the warden readme. Or look right in the lib/warden.rb
I put
Warden.test_mode!
in place of the env call at the /test path and get a nice blank page at
http://localhost:9292/test
Some bloggers have stated that there isn't a lot of documentation for warden but I disagree. There is a whole wiki. see https://github.com/hassox/warden/wiki
Take it slow and find out how to use middleware in Rack. Here's a very good article https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware
I think maybe you might want to start out with tests as I found a good example and you could use it with your app.
ENV['RACK_ENV'] = 'test'
require 'test/unit'
require 'rack/test'
require File.dirname(__FILE__) + '/web'
class AuthenticationTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
WardenTest #MyApp
end
def test_without_authentication
get '/protected'
assert_equal 401, last_response.status
end
def test_with_bad_credentials
authorize 'bad', 'boy'
get '/protected'
assert_equal 401, last_response.status
end
def test_with_proper_credentials
authorize 'admin', 'admin'
get '/protected'
assert_equal 200, last_response.status
assert_equal "You're welcome, authenticated client", last_response.body
end
end
Then a few routes added to your app.
helpers do
def protected!
return if authorized?
headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
halt 401, "Not authorized\n"
end
def authorized?
#auth ||= Rack::Auth::Basic::Request.new(request.env)
#auth.provided? and #auth.basic? and #auth.credentials and
#auth.credentials == ['admin', 'admin']
end
end
get '/' do
"Everybody can see this page"
end
get '/protected' do
protected!
"You're welcome, authenticated client"
end
In my experience working with Ruby, it's always a good idea to start out with tests for any new project. I often test little pieces first though just to gain an understanding of how they work.
Once you get a better understanding of Rack, especially Rack::Builder, you can use
map '/test' do
...all the middleware needed
run App
end
and try out different configurations to see which ones work best for your needs as I'm doing while I write this.
Enjoy! ;-)

Why am I getting FactoryGirl wrong number of arguments error?

Having strange behavior from FactoryGirl in non-rails app. getting wrong number of arguments error...don't know why.
gideon#thefonso ~/Sites/testing (master)$ rspec spec
/Users/gideon/.rvm/gems/ruby-1.9.3-p194/gems/factory_girl-4.1.0/lib/factory_girl/syntax/default.rb:6:in `define': wrong number of arguments (1 for 0) (ArgumentError)
from /Users/gideon/Sites/testing/spec/factories.rb:1:in `<top (required)>'
Here are the files involved...
login_user_spec.rb
require_relative '../spec_helper'
require_relative '../lib/login_user'
describe "Existing User Log in Scenario", :type => :request do
before :all do
#user = FactoryGirl(:user)
end
xit "should allow user to select login from top nav" do
visit "/"
within("#main-header")do
click_link 'Log In'
end
page.should have_content('Log in to your account')
end
it "and fill in login form" do
visit "/login"
within("#login-form")do
fill_in 'user-email', :with => #user.email
fill_in 'user-password', :with => #user.password
end
#FIXME - the design crew will make this go away
within("#login-form") do
click_link '#login-link' #this gives false failing test...geek query...why?
end
page.should have_content('Manage courses')
end
end
Factories.rb
FactoryGirl.define :user do |u|
u.email "joe#website.com"
u.password "joe009"
end
user.rb
class User
attr_accessor :email, :password
end
spec_helper.rb
require 'rubygems'
require 'bundler/setup'
require 'capybara'
require 'rspec'
require 'capybara/rspec'
require 'json'
require 'capybara/dsl'
# Capybara configuration
Capybara.default_driver = :selenium
Capybara.app_host = "http://www.website.com"
require 'factory_girl'
# give me ma stuff
FactoryGirl.find_definitions
require "rspec/expectations"
include Capybara::DSL
include RSpec::Matchers
ANSWER: :user is a reserved word..changed it to :stuff...works fine now.
Try to use new syntax from readme
FactoryGirl.define do
factory :user do
email "joe#website.com"
password "joe009"
end
end
If this is a non-rails app you will have to create a Userclass first. I'm not sure if you have to have instance vars with name, password and email but you definitely will have to have that class defined somewhere.
See the Getting Started file on Github for more on that.
Try this
FactoryGirl.define do
factory :user do |u|
u.email "joe#website.com"
u.password "joe009"
end
end

Capybara testing of sinatra app fails

I am trying to test a sinatra app using minitest and capybara but get several errors on all tests using capybara features like fill_in or visit.
test_index gives:
undefined local variable or method `app' for #
test_create_user gives:
Invalid expression: .////form[#id = 'register']
test_same_email gives:
Unable to find css "#register"
test_login gives:
cannot fill in, no text field, text area or password field with id,
name, or label 'email' found
Any suggestions on what may be wrong?
test.rb
require "test/unit"
require "minitest/autorun"
require "capybara"
require "capybara/dsl"
require "rack/test"
require_relative "../lib/kimsin.rb"
ENV["RACK_ENV"] = "test"
class KimsinTests < Test::Unit::TestCase
include Rack::Test::Methods
include Capybara::DSL
Capybara.app = Sinatra::Application
def test_index
visit "/"
assert stuff..
end
def test_create_user
visit "/user/new"
within "//form#register" do
fill_in :username, :with => "first#company.com"
fill_in :password, :with => "abC123?*"
fill_in :confirm_password, :with => "abC123?*"
click_link "Register"
end
assert stuff..
end
end
I'm using cygwin 1.7.15-1 on windows 7, rvm -v 1.14.1 (stable) and ruby -v 1.9.2p320.
----UPDATE----
Finally I got the tests work by incorporating Steve's suggestions:
within "form#register" do
fill_in "email", :with => "first#company.com"
click_button "Register"
and asserting the response by using capybara_minitest_spec:
page.must_have_content "Password"
page.must_have_button "Register"
I just answered a more recent question you posted about Sintra with Webrat.
Acceptance testing of sinatra app using webrat fails
I think the problem here is the same. Try replacing:
Capybara.app = Sinatra::Application
with:
Capybara.app = Kimsin
Personally I would choose Capybara over Webrat.

"No tests." error in ruby unit tests

My original test for user.rb looks like this:
require "test/unit"
require "minitest/autorun"
require "rack/test"
require_relative "../lib/kimsin.rb"
ENV['RACK_ENV'] = 'test'
class UserTests < Test::Unit::TestCase
include Rack::Test::Methods
include Kimsin
def app
Sinatra::Application
end
def test_user
#user = User.create :username => "barerd", :password => "abcdef"
get "/users"
assert_equal #user.username, "barerd"
refute_match #user.password, "abcdef"
end
end
The test ran and obviously failed as there was no User class. When I added the User class like below:
module Kimsin
require "data_mapper"
require "dm-migrations"
DataMapper.setup :default, "sqlite:///users.db"
class User
include DataMapper::Resource
include BCrypt
property :id, Serial
property :username, String, :required => true
property :password, String, :required => true
property :salt, String, :default => "876587349506434245565664566"
property :crypto, String, :default => BCrypt::Password.create password + salt
end
User.auto_migrate!
end
it throws a "No tests." error. Actually, not only this one but all tests throw the same error now. I suspected that this has sth to do with ruby in general, because it happened after I gem installed dm-core and at the beginning it threw an error:
"Error loading RubyGems plugin "/home/barerd/.rvm/gems/ruby-1.9.3-p125/gems/rubygems-bundler-0.2.8/lib/rubygems_plugin.rb": cannot load such file -- rubygems_bundler/rubygems_bundler_installer (LoadError)"
But when I try to run tests of other apps, they all work fine.
I use rvm 1.11.6 (stable) and ruby 1.9.3p125 (2012-02-16 revision 34643) [i386-cygwin] on a windows 7 by the way. Any clue to the error?
To note, the core module file kimsin.rb is as follows:
require "sinatra"
require "erb"
require "bcrypt"
require_relative "../lib/kimsin/version"
require_relative "../lib/kimsin/user"
use Rack::Session::Pool, :expire_after => 2592000
set :session_secret, "n9c0431qt043fcwo4ponm3w5483qprutc3q9pfw3r0swaypedx2qafec2qdomvuj8cy4nawscerf"
module Kimsin
get "/" do
title = "Kimsin?"
erb :index, :locals => {:title => title}
end
end

Sinatra/CouchDB error?

I'm working on my first Sinatra/CouchDB project and I'm getting an error I can't explain.
Here's my rackup (config.ru) file:
require 'rubygems'
require 'couchrest'
require 'patina'
set :environment, :development
set :root, File.dirname(__FILE__)
set :run, false
FileUtils.mkdir_p 'log' unless File.exists?('log')
log = File.new("log/sinatra.log", "a")
$stdout.reopen(log)
$stderr.reopen(log)
set :db, CouchRest.database!("http://127.0.0.1:5984/test")
run Sinatra::Application
And here's the app file (patina.rb):
require 'rubygems'
require 'sinatra'
require 'couchrest'
require 'haml'
class Article < CouchRest::ExtendedDocument
use_database settings.db
property :title
timestamps!
view_by :title
end
get '/' do
#db = settings.db
haml :index
end
Without the class definition in patina.rb, the route returns a page that displays the #db property as I was expecting. However, when I add the class definition to patina.rb I get "Ruby (Rack) application could not be started" error message.
Obviously this has something to do with my class definition, but I can't figure out what the problem is and the error message doesn't seem that helpful to me.
Also, I'd actually prefer to have the class definition in a separate file (Article.rb), but I can't figure out how to do that in the context of my Sinatra app.
Any help would be greatly appreciated!
EDIT:
See my answer below.
After a lot of googling, I discovered that the 1.4 series of json.gem are known to cause a lot of problems. I uninstalled all the json gems I had and installed json-1.2.4.gem instead. I have everything working correctly now. Here's the setup I'm using:
config.ru (Rackup file):
require 'application'
set :environment, :production
set :root, File.dirname(__FILE__)
set :run, false
FileUtils.mkmdir_p 'log' unless File.exists?('log')
log = File.new('log/sinatra.log', 'a+')
$stdout.reopen(log)
$stderr.reopen(log)
run Sinatra::Application
environment.rb:
require 'rubygems'
require 'couchrest'
require 'haml'
require 'ostruct'
require 'sinatra' unless defined?(Sinatra)
configure do
SiteConfig = OpenStruct.new(
:title => 'Application Title',
:author => 'Your Name',
:url_base => 'Your URL',
:url_base_db => 'Your CouchDB Server',
:db_name => "Your DB Name"
)
# load models
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
Dir.glob("#{File.dirname(__FILE__)}/lib/*.rb") { |lib| require File.basename(lib, '.*') }
end
lib/contact.rb (Model example, models auto-loaded in environment.rb):
class Contact < CouchRest::ExtendedDocument
include CouchRest::Validation
use_database CouchRest.database!((SiteConfig.url_base_db || '') + SiteConfig.db_name)
property :name
timestamps!
view_by :name
end
application.rb:
require 'rubygems'
require 'sinatra'
require 'environment'
configure do
set :views, "./views"
end
error do
e = request.env['sinatra.error']
Kernel.puts e.backtrace.join("\n")
'Application error'
end
helpers do
end
get '/new/?' do
haml :new
end
post '/save/?' do
#contact_name = params[:contact_name]
#contact = Contact.new
#contact.name = #contact_name
#contact.save
haml :save
end
get '/' do
haml :index
end
Hope this helps someone in the future!
try requiring 'patina' after setting :db. I think the class body of Article is executing the use_database method before the setting exists.
you should be able to put Article in article.rb (ruby naming convention is UpperCamel for classes, but under_scores for the files in which classes are defined) and then require 'article' in patina.rb.
thats the only thing that stood out for me, so let me know if that works.

Resources