I'm very new to TDD, and have opted to go with the above mentioned Gems. I think I have set it up correctly as I can run my tests. I can't, however, figure out how to populate my test database from db/seeds.rb. When I invoke
rake db:seed RAILS_ENV=test
in the terminal, I can see the rows created in the database through PGAdmin. However, when I run my tests with the following
rake minitest:all
the database ends up being blank afterwards, and in the test when I save a screenshot, the items from the database does not appear in the frontend as it does when I'm in dev.
My test_helper.rb contains the following.
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require 'minitest/rails/capybara'
require 'minitest/focus'
require 'minitest/colorize'
Capybara.javascript_driver = :webkit
class ActiveSupport::TestCase
fixtures :all
DatabaseCleaner.strategy = :transaction
class MiniTest::Spec
before :each do
Rake::Task["db:seed"].invoke
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
end
And for some extra background, my db/seeds.rb file (which works when seeded from manually using rake)
ProgramIndustry.delete_all
ProgramIndustry.create([
{ name: 'Accounting and finance'},
{ name: 'Banking'},
{ name: 'Construction'},
{ name: 'Education'}
])
Why would the database not be populated with seeds.rb when the tests start?
Your database is blank because you are using DatabaseCleaner, which removes the data from the database. I assume this is what you want your test_helper.rb file to look like:
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require 'minitest/rails/capybara'
require 'minitest/focus'
require 'minitest/colorize'
Capybara.javascript_driver = :webkit
class ActiveSupport::TestCase
fixtures :all
DatabaseCleaner.strategy = :transaction
before do
DatabaseCleaner.start
Rake::Task["db:seed"].invoke # seed after starting
end
after do
DatabaseCleaner.clean
end
end
I don't know about invoking the db:seed task from the before hook, that seems kinda suspect. But I don't use DatabaseCleaner, as I prefer to use fixtures and the transactions supported by ActiveSupport::TestCase.
I don't know why you are using DatabaseCleaner, but seeing as you are using RSpec syntax in Minitest, I'm assuming you are just trying things until they work. Might I suggest the dropping DatabaseCleaner and put all your test data in fixtures and using the following to manage the database transactions across threads:
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require 'minitest/rails/capybara'
require 'minitest/focus'
require 'minitest/colorize'
class ActiveSupport::TestCase
fixtures :all
end
# Capybara driver
Capybara.javascript_driver = :webkit
# Make all database transactions use the same thread
ActiveRecord::ConnectionAdapters::ConnectionPool.class_eval do
def current_connection_id
Thread.main.object_id
end
end
And if you have issues with that, consider this variation:
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require 'minitest/rails/capybara'
require 'minitest/focus'
require 'minitest/colorize'
class ActiveSupport::TestCase
fixtures :all
end
# Capybara driver
Capybara.javascript_driver = :webkit
# Make all database transactions use the same thread
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
Related
I have used RSpec in conjunction with Capybara and Capybara-webkit on many Rails projects and it usually works smoothly. For some reason I'm having problems configuring the js: true feature specs to work this time around. They are not interacting with the database properly. I use factory_girl and database_cleaner to manage the test db content. I have DatabaseCleaner.strategy = :truncation in database_cleaner.rb for the js: true tests but it still doesn't work. Oddly, when I put in ActiveRecord::Base.establish_connection into databse_cleaner.rb the tests work fine. Why??
Here is rails_helper.rb:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if
Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!
Capybara::Webkit.configure(&:block_unknown_urls)
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
This is the important parts of spec_helper.rb:
ENV['RAILS_ENV'] ||= 'test'
ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup'
Bundler.require
require 'pry-byebug'
require 'capybara/rspec'
require 'capybara/webkit'
require 'database_cleaner'
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
# use `describe 'Feature', type: :feature, js: true` to use this driver
Capybara.javascript_driver = :webkit
# tests use regular (faster) driver if they don't require js
Capybara.default_driver = :rack_test
Capybara::Webkit.configure do |config|
config.allow_unknown_urls
end
RSpec.configure do |config|
config.after(:suite) do
FileUtils.rm_rf(Dir["#{Rails.root}/spec/test_files/"])
end
config.include Capybara::DSL
...
This is database_cleaner.rb (located in spec/support):
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
When I change this, the tests can interact with the db properly:
config.before(:each, js: true) do
ActiveRecord::Base.establish_connection
DatabaseCleaner.strategy = :truncation
end
Why? I shouldn't have to explicitly tell it to establish a db connection.
Your question states that you're using :transaction for your js tests, but the code shows :truncation. Truncation is what you should be using for your js tests so I assume the question is a typo.
You should be using the recommended database_cleaner - https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example - which detects whether truncation is needed based on the driver being used rather than the 'js: true' metadata and also uses append_after rather than just after, which is very important for test stability.
I also don't see a require 'capybara/rails' anywhere in your rails_helper or spec_helper
When you set js: true, you're essentially just using Capybara which in essence is a Selenium-driven browser request. Any code running outside the test process (like this Selenium browser request) does not see the database fixture.
Preferable to what you're doing though, is to share the data state across the Selenium web server and test the code itself.
Somewhere in Rspec.config (possibly your database_cleaner.rb), you'll want to set
config.use_transactional_fixtures = false
"Everyday Rails Testing with Rspec" has a trick that was sometimes needed for certain test scenarios where you need to share the same DB connection before Rails 5. Although it can cause other issues as pointed out in comments below:
Make a spec/support/shared_db_connection.rb file and add this content:
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
I am following the rails api book but building the code in an engine. The test is at spec/controllers/concerns/handicap/authenticable_spec.rb and looks like this
require 'spec_helper'
require_relative '../../../../app/controllers/concerns/handicap/authenticable.rb'
class Authentication
include Handicap::Authenticable
end
module Handicap
describe Authenticable, type: :controlller do
let(:authentication) { Authentication.new }
subject { authentication }
describe "#current_user" do
before do
#user = FactoryGirl.create :handicap_user
request.headers["Authorization"] = #user.auth_token
authentication.stub(:request).and_return(request)
end
it "returns the user from the authorization header" do
expect(authentication.current_user.auth_token).to eql #user.auth_token
end
end
end
end
When I run the test directly i.e. rspec ./spec/controllers/concerns/handicap/authenticable_spec.rb I get an error:
uninitialized constant Handicap::FactoryGirl
However, when I run all the tests i.e. rspec spec, it does find the FactoryGirl constant and the test fails with
undefined local variable or method `request' for #<RSpec::ExampleGroups::HandicapAuthenticable::CurrentUser:0x007ff276ad5988>.
According to this github issue, I need to add < ActionController::Base to the Authentication class i.e.
class Authentication < ActionController::Base
but if I add this in, I get
uninitialized constant ActionController
I have also tried adding < Handicap::ApplicationController but get
uninitialized constant Handicap::ApplicationController
There appears to be something wrong with my namespacing. There are three symptoms, the fact that FactoryGirl cannot be found if I run the test by itself, but is found when all the tests are run. The second is that it cannot find ActionController even when all the tests are run. The third is that I need to add the line:
require_relative '../../../../app/controllers/concerns/handicap/authenticable.rb'
to find the module that is being tested.
How do I fix my namespacing?
The rails_helper.rb file is
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../dummy/config/environment.rb', __FILE__)
require 'rspec/rails'
require 'capybara'
require 'capybara/rails'
require 'capybara/rspec'
require 'capybara-screenshot'
require 'capybara-screenshot/rspec'
require 'capybara/poltergeist'
require 'capybara/email/rspec'
require 'pp'
require 'chris_api_helpers'
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'factory_girl_rails'
ActiveRecord::Migration.maintain_test_schema!
Shoulda::Matchers.configure do |config|
config.integrate do |with|
# Choose a test framework:
with.test_framework :rspec
with.library :rails
end
end
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# force test migrations for db:migrate
ActiveRecord::Migration.maintain_test_schema!
Capybara::Screenshot.prune_strategy = { keep: 20 }
Capybara::Screenshot.append_timestamp = false
config.include FactoryGirl::Syntax::Methods
FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
FactoryGirl.find_definitions
config.include Devise::Test::ControllerHelpers, type: :controller
end
and the spec_helper.rb is
require 'simplecov' if ENV["COVERAGE"]
SimpleCov.start do
add_filter '/spec/'
add_filter '/config/'
add_filter '/lib/'
add_filter '/vendor/'
add_group 'Controllers', 'app/controllers'
add_group 'Models', 'app/models'
add_group 'Helpers', 'app/helpers'
add_group 'Mailers', 'app/mailers'
add_group 'Views', 'app/views'
end if ENV["COVERAGE"]
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
#http://stackoverflow.com/questions/30859037/suppress-backtrace-for-rspec-3
config.backtrace_exclusion_patterns = [
/\/lib\d*\/ruby\//,
/bin\//,
/gems/,
/spec\/spec_helper\.rb/,
/lib\/rspec\/(core|expectations|matchers|mocks)/
]
end
You should not be putting specs into modules. This is the cause of problem here. If you need to reference a namespaced class, reference it like RSpec.describe Handicap::Authenticatable.
In general, when you are within a namespace and need to reference something explicitly from the 'root' scope, you can prepend it with double-colons. Such as:
module Handicap
class Something
def do_stuff
::FactoryGirl.create(:person)
end
end
end
It turns out that at the top of my file I should have had require 'rails_helper', not require 'spec_helper'``. All my other files hadrequire 'rails_helper'`` so when I ran the whole test suite, the rails_helper was being loaded anyway.
Embarrassing, but this Q&A might help someone else that has trouble spotting simple errors.
I'm getting this strange error when I try to run my test in rails:
postgresql_adapter.rb:592:in `async_exec': PG::UndefinedTable: ERROR: relation "users" does not exist (ActiveRecord::StatementInvalid)
I don't know if this helps but my spec_helper.rb file looks like this:
require 'rubygems'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'factory_girl_rails'
require 'database_cleaner'
require 'capybara/rails'
require 'capybara/rspec'
require 'capybara/poltergeist'
require 'support/mailer_macros'
require 'support/test_helper'
require 'support/factory_girl_helper'
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new app, window_size: [1600, 1200], js_errors: false
end
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.include(FactoryGirlHelper)
config.include(MailerMacros)
config.include(TestHelper)
config.before(:each) { reset_email }
config.expect_with :rspec do |c|
c.syntax = [:should, :expect]
end
Capybara.javascript_driver = :poltergeist
config.include Capybara::DSL
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation) # moving to before :each doesn't help
DatabaseCleaner.strategy = :truncation # moving to before :each doesn't help
end
config.around :each do |example| # refactoring as before/after with .start/.clean doesn't help
DatabaseCleaner.cleaning { example.run }
end
end
Anyone has any idea why this is happening? The application in the browser seems to be working fine.
Sounds like the schema for you test db is out of sync, you either need to run migrations on it or just reset/recreate it.
Ok I figured it out following the answer here: FactoryGirl screws up rake db:migrate process
So the problem was coming from factorygirl. I updated my Gemfile to look like this:
gem "factory_girl_rails", :require => false
And then also add this:
require 'factory_girl_rails'
to my spec_helper.rb file and that fixed all the issues both localy and on circleci :)
For a while I was using Selenium / Spork / Rspec, with cache_classes on false, and everything seemed to be working.
In switching over to webkit, I've started to get errors related to cache_classes (e.g. Expected User, got User), so I've been fighting with it to try to get cache_classes set to true.
However no matter what I do, I end up with the following error:
Capybara::Driver::Webkit::WebkitInvalidResponseError:
Unable to load URL: http://127.0.0.1:56398/login
I have tried all kinds of things... including:
ActiveSupport::Dependencies.clear in both the prefork and each _run blocks
The code here: http://my.rails-royce.org/2012/01/14/reloading-models-in-rails-3-1-when-usign-spork-and-cache_classes-true/
Starting to wonder if I should just live with cache_classes = false, and figure out how to avoid the Factory girl errors. Any help would be appreciated. My spork file is as follows:
require 'spork'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rails'
require 'capybara/rspec'
require 'database_cleaner'
require 'factory_girl'
require 'authlogic/test_case'
require 'email_spec'
include Authlogic::TestCase
require File.expand_path("../../config/environment", __FILE__)
RSpec.configure do |config|
# == 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
ApplicationController.skip_before_filter :activate_authlogic
config.include(EmailSpec::Helpers)
config.include(EmailSpec::Matchers)
config.include Capybara::DSL
# 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.before(:suite) do
DatabaseCleaner.strategy = :truncation, {:except => %w[ages coefficients weights1 weights2 weights3 weights4 weights5 weights6 weights7 weights8 etfs]}
DatabaseCleaner.clean
end
config.before(:each) do
DatabaseCleaner.start
Capybara.current_driver = :webkit if example.metadata[:js]
#Capybara.current_driver = :selenium if example.metadata[:js]
activate_authlogic
ActiveRecord::Base.instantiate_observers
end
config.after(:each) do
DatabaseCleaner.clean
Capybara.use_default_driver if example.metadata[:js]
end
Capybara.default_selector = :css
end
# 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}
## SUPPORT METHODS ##
## (erased for clarity) ##
## ##
ActiveSupport::Dependencies.clear
end
Spork.each_run do
#FactoryGirl.reload
# Required to fix a recurring error when testing Active_Admin stuff
# See here: http://railsgotchas.wordpress.com/2012/01/31/activeadmin-spork-and-the-infamous-undefined-local-variable-or-method-view_factory/
# Delete at some point if active admin or whoever fixes this
ActionView::Template.register_template_handler :arb, lambda { |template|
"self.class.send :include, Arbre::Builder; #_helpers = self; self.extend ActiveAdmin::ViewHelpers; #__current_dom_element__ = Arbre::Context.new(assigns, self); begin; #{template.source}; end; current_dom_context"
}
#ActiveSupport::Dependencies.clear
end
UPDATE : Adding an example spec just in case it helps....
describe "Items" do
before(:each) do
#user = Factory.create(:user)
activate_authlogic
b = # something not important
end
describe "usage paths" do
it "the form directly from the basic_simulation show page should have correctly functioning javascript validation", :js => true do
request_sign_in(#user) # This is a helper method which goes through the login form
visit '/basic_simulation'
fill_in "amount", :with => "-5000"
click_button "Calculate"
page.should have_selector("label.jquery-validator.amount-error", :text => "Please enter a value greater than or")
fill_in "amount", :with => "5000"
click_button "Calculate"
page.should have_selector("input#amount", :value => "5000")
end
end
You are having issues due to threading problems with Capybara-webkit and the test suite.
Jose Valim explains it much more clearly in a recent blog post.
If you follow his recommendations then you should be able to turn on transactional fixtures, remove database cleaner altogether and no longer have issues with your data during tests while using capybara-webkit. You'll get a nice boost in testing performance as well.
The trick though is to make sure that Jose's suggestion is in the Spork.each_run block or it will not work. For clarity here are the relevant parts of my spec_helper.rb.
require 'spork'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rspec'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.mock_with :rspec
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
Capybara.default_driver = :rack_test
Capybara.default_selector = :css
Capybara.javascript_driver = :webkit
end
end
Spork.each_run do
if Spork.using_spork?
ActiveRecord::Base.instantiate_observers
end
require 'factory_girl_rails'
# Forces all threads to share the same connection, works on Capybara because it starts the web server in a thread.
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
end
A few other small suggestions:
If you are using the latest version of factory_girl_rails then you
should be using require factory_girl_rails in the Spork.each_run
block and require factory_girl should be removed from the prefork
The latest factory_girl_rails also no longer requires ActiveSupport::Dependencies.clear at all, although some people are still having issues without it so you should test removing it.
I'm still not sure about the need for ActiveRecord::Base.instantiate_observers but in any case you would only need it if you are using observers and I understand that it should be in the each_run block.
Try all that and see if it works for you.
I'm trying to reset the "sequence" in factory girl between each test I run.
(factory_girl 2.6.0 and factory_girl_rails 1.7.0)
I think that to do so, I have to reload the FactoryGirl definitions. I do it in the last lines of spec_helper.rb:
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'
require 'capybara/rspec'
require "rails/application"
Spork.trap_method(Rails::Application::RoutesReloader, :reload!)
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.mock_with :rspec
require 'database_cleaner'
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.infer_base_class_for_anonymous_controllers = false
# For mailer
config.include(MailerMacros)
config.before(:each) {reset_email}
end
end
Spork.each_run do
# This code will be run each time you run your specs.
I18n.backend.reload!
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
require 'factory_girl'
FactoryGirl.definition_file_paths = [File.join(Rails.root, 'spec', 'factories')]
FactoryGirl.find_definitions
end
Adding:
FactoryGirl.definition_file_paths = [File.join(Rails.root, 'spec', 'factories')]
FactoryGirl.find_definitions
Leads me to the following error when running rspec spec/
→ bundle exec guard
Guard uses Growl to send notifications.
Guard is now watching at '/Rails/projects/MyRailsProject'
Starting Spork for RSpec
Using RSpec
Preloading Rails environment
Loading Spork.prefork block...
Spork is ready and listening on 8989!
Spork server for RSpec successfully started
Guard::RSpec is running, with RSpec 2!
Running all specs
Exception encountered: #<FactoryGirl::DuplicateDefinitionError: Factory already registered: user>
Which seems to be a duplicated factory error, maybe it's trying to load factory girl twice, but I don't understand why.
It is trying to load all your factories twice, because you're asking it to.
Replace your call to find_definitions with
FactoryGirl.reload
which clear out existing factories, sequences etc and then call find_definitions for you