I'm trying to create a Rails 3 Engine using MongoMapper. I'm having a world of pain getting it going. Here is my model:
module GoodComments
class Comment
include MongoMapper::Document
key :comment, String
end
end
Super simple, I know! My config/routes.rb:
GoodComments::Engine.routes.draw do
resources :comments
end
I created a config/application.rb:
require File.expand_path('../boot', __FILE__)
module GoodComments
class Application < Rails::Application
config.generators do |g|
g.orm :mongo_mapper # :active_record
g.template_engine :erb # :haml
g.test_framework :rspec, :fixture => true, :views => false
g.fixture_replacement :factory_girl, :dir => "spec/factories"
end
end
end
I ran rails generate scaffold_controller Comment -o mongo_mapper and my controllers were generated. When I run my server and go to http://localhost:3000/good_comments/comments, I get an error:
LoadError in GoodComments::CommentsController#index
Expected /Users/shamoon/Sites/good_comments/app/models/comment.rb to define Comment
Rails.root: /Users/shamoon/Sites/good_comments/test/dummy
Any help?
It looks like your controller was expecting a class called Comment in comment.rb, so maybe the controller needs to be operating in the same module? Or you would just have to specify some non-default configurations or be more specific about which model the controller should use.
Also in my MongoMapper app I have a few more lines than you added to the top of config/application.rb:
require File.expand_path('../boot', __FILE__)
# from http://mongomapper.com/documentation/getting-started/rails.html
# replace:
# require 'rails/all'
# with:
require "action_controller/railtie"
require "action_mailer/railtie"
require "active_resource/railtie"
require "rails/test_unit/railtie"
# Uncomment for asset pipelining in Rails 3.1
# require "sprockets/railtie"
Related
Environment
Rails 4.2.0
ruby-2.2.1 [ x86_64 ]
devise 3.4.1
rspec-core 3.2.2
rspec-rails 3.2.1
In my /spec/rails_helper.rb I have included Devise helpers for spec files tagged with type: :controller and type: :request
spec/rails_helper.rb
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:suite) do
begin
DatabaseCleaner.start
FactoryGirl.lint
ensure
DatabaseCleaner.clean
end
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run # ==================> L-60
end
end
config.include FactoryGirl::Syntax::Methods
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
config.include Devise::TestHelpers, type: :controller
config.include Devise::TestHelpers, type: :request
end
With that config in place the type: controller specs runs fine. However when running type: request specs I am getting following error:
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `env' for nil:NilClass
# /home/.rvm/gems/ruby-2.2.1#myapp/gems/devise-3.4.1/lib/devise/test_helpers.rb:24:in `setup_controller_for_warden'
# ./spec/rails_helper.rb:60:in `block (3 levels) in <top (required)>'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/generic/base.rb:15:in `cleaning'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/base.rb:92:in `cleaning'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:86:in `block (2 levels) in cleaning'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:87:in `call'
# /home/.rvm/gems/ruby-2.2.1#simplyhomeapp/gems/database_cleaner-1.4.1/lib/database_cleaner/configuration.rb:87:in `cleaning'
# ./spec/rails_helper.rb:59:in `block (2 levels) in <top (required)>'
https://github.com/plataformatec/devise/blob/master/lib/devise/test_helpers.rb#L24 is following
def setup_controller_for_warden #:nodoc:
#request.env['action_controller.instance'] = #controller # ==================> L-24
end
I am aware that #request instance is not available for :request type specs and hence the error.
Are there any helpers available we can use to sign-in a user in :request type specs when using Devise?
I found a similar issue https://github.com/plataformatec/devise/issues/1114, the reply to which suggests following:
If you're doing integration tests, make sure to sign in your user in the tradicional way, by filling the sign in form and submitting.
But I would like to by pass the actual login for specs which requires a signed-in user.
Thanks.
With the help of a few SO posts(please refer to the References section below) I have managed to achieve the desired solution. I am posting my working code below, in case it can help others looking out for the same:
spec/rails_helper.rb
RSpec.configure do |config|
....
....
config.include Devise::TestHelpers, type: :controller
config.include Warden::Test::Helpers, type: :request
end
spec/shared_contexts.rb
RSpec.shared_context "api request global before and after hooks" do
before(:each) do
Warden.test_mode!
end
after(:each) do
Warden.test_reset!
end
end
RSpec.shared_context "api request authentication helper methods" do
def sign_in(user)
login_as(user, scope: :user)
end
def sign_out
logout(:user)
end
end
/spec/requests/api/logout_spec.rb
require 'rails_helper'
require 'shared_contexts'
RSpec.describe "Api Logout", :type => :request do
include_context "api request authentication helper methods"
include_context "api request global before and after hooks"
let(:email) { 'test_user1#example.com' }
let(:password) { 'password' }
# Assumes you have FactoryGirl included in your application's test group.
let!(:user) { create(:user, email: email, password: password) }
context "DELETE /logout" do
it "responds with 204 and signs out the signed-in user" do
sign_in(user)
# Till not figured out how to assert Warden has successfully logged in the user like we can do in a Devise controller spec by asserting subject.current_user. If anybody knows a way to do it please share.
# expect(subject.current_user).to_not be_nil
delete "/logout"
expect(response).to have_http_status(204)
end
end
end
I have still not figured out how to assert Warden has successfully logged in the user like we can do in a Devise controller spec by asserting expect(subject.current_user).to_not be_nil. If anybody knows a way to do it please share.
References
Integration test with rspec and devise sign_in env
https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
https://github.com/hassox/warden/blob/master/lib/warden/test/helpers.rb
undefined method 'env' for nil:NilClass
https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs
The code in the above link still relies on a request object which is only available in Controller specs. Thus not useful for type: :request specs.
https://github.com/plataformatec/devise/issues/3555
Thanks,
Jiggnesh
While the popular answer here, also replicated on the Devise wiki, is ok, it is simplest to just:
spec/rails_helper.rb
RSpec.configure do |config|
# ...
config.include Devise::Test::IntegrationHelpers, type: :request
end
And just use sign_in in your request spec. This is the equivalent of declaring include Devise::Test::IntegrationHelpers in an system/feature spec or Rails system/controller test.
Reference
Devise wiki: How to sign in and out a user in request type specs - Simple approach
I can't for the life of me seem to set up rspec/capybara in a way that doesn't throw random errors from time to time. My most common errors (but not consistent) are along the lines of:
An error occurred in an after hook
ActiveRecord::RecordNotFound: Couldn't find Post with id=1
occurred at [...]/gems/activerecord-3.2.11/lib/active_record/relation/finder_methods.rb:341:in `find_one'
And a close second is anything related to expecting model objects that were created within the spec and then not found later. e.g. something like
Sorter should populate featured_posts with appropriate posts
Failure/Error: Sorter.top_4.map(&:id).should eq(([best_new_post, best_old_post] + new_posts.last(2).reverse).map(&:id))
expected: [2, 1, 40, 38]
got: []
These tests will always pass when I run them individually, and they don't always fail when I run them in the suite. I'm assuming there is some sort of issue with threads and how the database is set up, but I can't seem to figure it out.
My spec_helper.rb is a hodgepodge of advice from various sources:
require 'rubygems'
require 'spork'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
require 'capybara/email/rspec'
include ApplicationHelper
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
include Warden::Test::Helpers
Warden.test_mode!
ActionController::Base.asset_host = "http://localhost:3000"
RSpec.configure do |config|
config.include Rails.application.routes.url_helpers
config.use_transactional_fixtures = false
config.use_transactional_examples = false #factoryGirl
# From http://railsapps.github.io/tutorial-rails-devise-rspec-cucumber.html
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) { DatabaseCleaner.start }
config.after(:each) { DatabaseCleaner.clean }
#
config.infer_base_class_for_anonymous_controllers = false
## Setting up email helpers
config.include MailerMacros
config.before(:each) { reset_email }
## Setting up test and login helpers for Devise testing
config.include Devise::TestHelpers, type: :controller
config.extend ControllerMacros, type: :controller
# From http://railscasts.com/episodes/413-fast-tests
config.order = "random"
config.include FactoryGirl::Syntax::Methods
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.filter_run_excluding :slow unless ENV["SLOW_SPECS"]
config.before(:all) { DeferredGarbageCollection.start }
config.after(:all) { DeferredGarbageCollection.reconsider }
end
end
Spork.each_run do
Rails.cache.clear
Warden.test_reset!
FactoryGirl.reload
end
I've tried removing pieces to troubleshoot, but it's hard to gauge whether it hss solved anything since the errors are sporadic.
Is there anything here that stands out as an obvious reason for the errors above, or any advice on how to troubleshoot this?
In your spec_helper.rb, try setting
config.use_transactional_fixtures = true
I'm using this solution to avoid issues with the database during javascript tests.
The first run through the suite, the tests run fine, all passing.
If I run the entire suite again, they'll still pass.
But, if I run an individual spec file and then try to run the suite (or other individual test), I get this error:
An error occurred in an after hook
ActiveRecord::StatementInvalid: ArgumentError: prepare called on a closed database: rollback transaction
occurred at /home/steveq/.rvm/gems/ruby-1.9.3-p194#rails32/gems/sqlite3-1.3.7/lib/sqlite3/database.rb:91:in `initialize'
1) Signing up with valid information
Failure/Error: visit "/sign_up"
ActiveRecord::StatementInvalid:
ArgumentError: prepare called on a closed database: PRAGMA table_info("users")
# ./app/controllers/registrations_controller.rb:3:in `new'
# ./app/controllers/registrations_controller.rb:3:in `new'
# ./spec/features/sign_up_feature_spec.rb:5:in `block (2 levels) in <top (required)>'
If I reload guard, the tests will pass again.
Does anyone have any insight into what's happening here or any possible solutions? I've tried every variation I can think of, and here's my spec_helper file to show the things I've tried (the variations are commented out, the current code - what's suggested on the capybara page - is what I'm using now).
require 'rubygems'
require 'spork'
#uncomment the following line to use spork with the debugger
#require 'spork/ext/ruby-debug'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
#Make it so Selenium (out of thread) tests can work with transactional fixtures
#REF http://opinionated-programmer.com/2011/02/capybara-and-selenium-with-rspec-and-rails-3/#comment-220
# ActiveRecord::ConnectionAdapters::ConnectionPool.class_eval do
# def current_connection_id
# # Thread.current.object_id
# Thread.main.object_id
# end
# end
# FactoryGirl short syntax
config.include FactoryGirl::Syntax::Methods
config.use_transactional_fixtures = true
# set up for use with :js => true.
# See http://stackoverflow.com/questions/8178120/capybara-with-js-true-causes-test-to-fail for more info
# config.before :suite do
# if Capybara.current_driver == :rack_test
# DatabaseCleaner.strategy = :transaction
# else
# DatabaseCleaner.strategy = :truncation
# end
# DatabaseCleaner.start
# end
# config.after do
# DatabaseCleaner.clean
# end
# standard RSPEC config
# config.before(:suite) :truncation
# else
# :transaction
# end do
# DatabaseCleaner.strategy = if example.metadata[:js]
# :truncation
# else
# :transaction
# end
# DatabaseCleaner.clean_with(:truncation)
# end
# config.before(:each) do
# DatabaseCleaner.start
# end
# config.after(:each) do
# DatabaseCleaner.clean
# end
# config.before(:each) do
# DatabaseCleaner.strategy = if example.metadata[:js]
# :truncation
# else
# :transaction
# end
# DatabaseCleaner.start
# end
# config.after(:each) do
# DatabaseCleaner.clean
# end
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run :focus => true
config.run_all_when_everything_filtered = true
config.include MailerMacros
config.include LoginMacros
config.before(:each) { reset_email }
config.include Devise::TestHelpers, :type => :controller
config.extend LoginMacros, :type => :controller
end
end
Spork.each_run do
# allows capybara JS tests to run in separate thread
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
# This code will be run each time you run your specs.
load "#{Rails.root}/config/routes.rb"
FactoryGirl.reload
# reload all the models
Dir["#{Rails.root}/app/models/**/*.rb"].each do |model|
load model
end
end
So here's what I figured out - hopefully it will help anyone else who gets into this same trouble.
Firstly, the method I was using:
Spork.each_run do
# allows capybara JS tests to run in separate thread
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
# This code will be run each time you run your specs.
load "#{Rails.root}/config/routes.rb"
FactoryGirl.reload
# reload all the models
Dir["#{Rails.root}/app/models/**/*.rb"].each do |model|
load model
end
end
works just fine, but not, it seems, with sqlite.
The quickest fix was simply to swap out sqlite as my test db for mysql. That solved everything.
Another solution, one that I'm really digging right now, was to drop spork completely in favor of Zeus.
You can check it out via the github link, but I'll tell you why I like it.
It has no necessary config - no spork block in the spec_helper, no spork block in the guardfile.
It also speeds up server and console initialization to under a second- not a huge deal, but very, very pleasant.
My test suite (191 examples so far) went from running in around 35 seconds to 17.5 seconds - half the time.
I urge you to check it out.
I inherited a Rails project and was asked to update it so I am migrating the Rails 2.2.2 project to Rails 3.2.
I went through a few migration tutorials and ran the rails upgrade script and it loads fine when the default /public/index.html is there.
I then went ahead and removed /public/index.html, so the app would point to the file indicated in routes.rb and then I get:
LoadError (Expected /var/www/vendor_sandbox/app/controllers/application.rb to define Application):
app/controllers/application_controller.rb:1:in `<top (required)>'
app/controllers/home_controller.rb:1:in `<top (required)>'
The file that is causing the error was from the original Rails 2.2.2 code base. I left it because there was no indication in the migration docs that I was reading that mentioned removing it but clearly something is wrong.
I find it strange that I now have a Rails 3 version of application.rb in /config and an application.rb in /app/controllers/
Not sure what to do with /app/controllers/application.rb.
Here are the files mentioned:
#### /app/controllers/application.rb
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
# See ActionController::RequestForgeryProtection for details
# Uncomment the :secret if you're not using the cookie session store
protect_from_forgery # :secret => 'mysecretkey'
# See ActionController::Base for details
# Uncomment this to filter the contents of submitted sensitive data parameters
# from your application log (in this case, all fields with names like "password").
# filter_parameter_logging :password
def authenticate
return true if session[:user_id].to_i > 0
session[:after_login] = params
redirect_to :controller => "login"
return false
end
def authenticate_admin
user = User.find(session[:user_id])
#nav = [{:title => "Home", :action => {:controller => "home"}}]
return true if user and user.is_admin?
redirect_to :controller => "login"
return false
end
def clean_date_for_4D date
return "00/00/00" if !date or date == ""
return date.strftime("%m/%d/%Y") if date.class.to_s == "Date"
return Date.parse(date).strftime("%m/%d/%Y") # assume it's a string
end
def pad text, length=20
while text.length < length do
text = text + " "
end
return text
end
end
#### /config/routes.rb
VendorSandbox::Application.routes.draw do
match '/' => 'home#index'
match '/:controller(/:action(/:id))'
end
#### /config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
if defined?(Bundler)
Bundler.require(*Rails.groups(:assets => %w(development test)))
end
module VendorSandbox
class Application < Rails::Application
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
config.active_record.whitelist_attributes = true
# Enable the asset pipeline
config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
# Session key
config.session_store(:cookie_store, {:key => '_vendor_sandbox_session', :secret => 'secretkey'
# Time zone
config.time_zone = 'Central Time (US & Canada)' #'UTC' # Had to change this to keep created_at from being 4 hours in advance!
config.active_record.default_timezone = 'Central Time (US & Canada)'
end
end
Trying renaming your app/controllers/application.rb to application_controller.rb.
I think Rails is expecting your controller to be named with a _controller suffix, and the application.rb you have in your controllers folder isn't following that convention.
I wanted to comment on #Zajn answer, but I don't have the required "50 reputation".
Just for reference, app/controllers/application.rb was renamed to application_controller.rb in Rails 2.3
http://guides.rubyonrails.org/2_3_release_notes.html#application-controller-renamed
Spent whole day with the problem.
Very simple test. I want to visit main page and check if it has text "Match".
require 'spec_helper'
describe "Sign in" do
it "should sign in with correct data", :js => true do
visit root_path
page.should have_content("Match")
end
end
Running rake spec:requests I've got:
1) Sign in should sign in with correct data
Failure/Error: visit root_path
Timeout::Error:
Timeout::Error
# ./spec/requests/signin_spec.rb:5:in `block (2 levels) in <top (required)>'
Can't fix it. It doesn't work with or without spork and guard, and making me crazy :(
My environment:
Gemfile:
group :test, :development do
gem 'rspec-rails', '2.7.0' , :branch => 'rails3'
gem 'capybara'
gem 'guard'
gem 'guard-rspec'
gem 'guard-spork'
gem 'growl'
gem 'launchy'
gem 'growl_notify', :require => false if RUBY_PLATFORM =~/darwin/i
gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~/darwin/i
end
spec_helper.rb:
require 'rubygems'
require 'spork'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
# Add this to load Capybara integration:
require 'capybara/rspec'
require 'capybara/rails'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# Loading more in this block will cause your tests to run faster. However,
# if you change any configuration or code from libraries loaded here, you'll
# need to restart spork for it take effect.
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
#config.treat_symbols_as_metadata_keys_with_true_values = true
#config.filter_run :focus => true
#config.run_all_when_everything_filtered = true
end
end
Spork.each_run do
# This code will be run each time you run your specs.
FactoryGirl.reload
end