There are comments in the rails codebase that indicate that the test database should be reset between runs
rake -T
rake test:all # Run tests quickly by merging all types and not resetting db
rake test:all:db # Run tests quickly, but also reset db
config/database.yml
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
This doesn''t seem to be the case for me.
I'm using factory girl generate test models, here is an example factory
FactoryGirl.define do
factory :podcast do
sequence(:title) { |n| "Podcast #{n}" }
sequence(:feed_url) { |n| "http://podcast.com/#{n}" }
end
end
The podcast should have a unique feed_url so I validate it's uniqueness in the model.
class Podcast < ActiveRecord::Base
validates :feed_url, uniqueness: true, presence: true
end
In test_helper.rb I lint all factories
ENV["RAILS_ENV"] ||= "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'minitest/autorun'
FactoryGirl.lint
My test creates a podcast, builds another with the same name, then asserts that the second
is invalid.
require 'test_helper'
describe Podcast do
describe '#feed_url' do
it 'must be unique' do
podcast = create(:podcast)
new_podcast = build(:podcast, feed_url: podcast.name)
assert_invalid podcast, :feed_url, 'has already been taken'
end
end
end
The first time I run the tests it executes without errors and the tests all pass.
The second time I run the tests the Factory Girl lint fails because podcast feed_url has already been taken.
Why isn't the test database being rebuilt between runs?
We have a more involved FactoryGirl set up that prepares our database with some canonical items, but I think you could probably put this code directly in your test_helper.rb to assure the database is emptied:
# Destroy all models because they do not get destroyed automatically
(ActiveRecord::Base.connection.tables - %w{schema_migrations}).each do |table_name|
ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{table_name};"
end
Alternatively, run rake db:test:prepare before every run.
There is a gem too that you can use, but I don't have any experience with it: http://rubygems.org/gems/database_cleaner.
The reason the database is not resetting is that you are running your tests outside of the database transaction that rails provides. The ActiveSupport::TestCase class is the basis for all rails tests. ActiveRecord adds a per-test database transaction to this class. This transaction will reset the database after each test. But you aren't running your tests with ActiveSupport::TestCase, you are running your tests with Minitest::Spec which isn't configured to run the transaction.
The simplest solution is to add minitest-rails to your Gemfile, and change the require in your test_helper.rb file from minitest/autorun to minitest/rails. If you would rather add your own support for Minitest's spec DSL you can use this article as a starting point.
Do you have another factory that might be creating a podcast via an association?
FactoryGirl linting builds each factory and checks it's validity, and if another factory has a podcast as an association, it'll create a podcast record.
FactoryGirl recommends clearing the database after running the linting. They use database_cleaner in their example:
https://github.com/thoughtbot/factory_girl/blob/2bf15e45305ac03315cf2ac153db523d3ce89ce1/GETTING_STARTED.md#linting-factories
If you're using 'Rspec' to be your unit test framework. After the installation of gem 'rspec-rails', you will got one configuration file called: spec/rails_helper.rb and within it you will find one configuration which looks like this:
# 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 = true
this means that if it is true, then your each teat case will be running in a separate transaction.
Related
Using
Ruby "2.7.1"
Rails "6.0.3.1"
Mac OS Catalina 10.15.5
Using the default Rails Minitest and FactoryBot for test data.
Issue: I have test data being shared between system test files and hence failing tests. When I run the system test files individually the tests pass but this is inconvenient.
I use the the system test config out of the box as in the application_system_test_case.rb
This is my system test scenario.
I have 3 Customers in customers_system_test.rb file and 1 Customer (so far) in job_description_system_test.rb Customer 1 appears in the customers_system_test.rb failing test screenshots that Rails conveniently provide.
Note: The 3 failing tests all concern record counts such as assert_equal Customer.count, 3
The 3 failing tests
test "visit create customer and navigate back" do
...
assert_equal Customer.count, 3
end
test "cancel destroying a customer" do
assert_equal Customer.count, 3
...
end
test "destroy a customer" do
assert_equal Customer.count, 3
...
end
Interestingly I had the same issue with test data not being cleaned up when running rails test for controller, integration tests etc: The way I get around that is adding the following snippet to the test_helper.rb :
(ActiveRecord::Base.connection.tables - %w{schema_migrations}).each do |table_name|
ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{table_name} CASCADE;"
end Doesn't work for system tests
I feel like I am missing the obvious?
test_helper.rb
require "simplecov"
SimpleCov.start "rails"
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
class ActiveSupport::TestCase
include FactoryBot::Syntax::Methods
include Warden::Test::Helpers
# Run tests in parallel with specified workers
# parallelize(workers: :number_of_processors)
parallelize(workers: 1)
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
# fixtures :all
# Add more helper methods to be used by all tests here...
include Devise::Test::IntegrationHelpers
end
(ActiveRecord::Base.connection.tables - %w{schema_migrations}).each do |table_name|
ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{table_name} CASCADE;"
end
Ok I got all tests running and passing including system tests by doing the following (wasn't aware this was necessary in Rails 6)
...
setup do
sign_in user
#customer_1 = FactoryBot.create(:customer_with_job_descriptions)
#job_description_2 = FactoryBot.create(:job_description, customer_id: #customer_1.id)
end
teardown do
#customer_1.destroy!
#job_description_2.destroy!
end
...
Ref: 8 Setup and Teardown
https://guides.rubyonrails.org/v3.2/testing.html#setup-and-teardown
........................................................................................
Finished in 22.971069s, 4.3968 runs/s, 21.4182 assertions/s.
101 runs, 492 assertions, 0 failures, 0 errors, 0 skips
I populate a database that I want to run rspec tests against.
Rspec seems to delete the db before it starts the tests. How can I tell Rspec not to delete this test_db?
I populate the db outside of Rails / Rspec environment. To build all the factories needed to build up the DB is just not practical for the scope of the project but I would like to test against existing DB.
You can manage clean your database using database cleaner gem, and The documentation recommends the following configuration for rspec as this link, but you change as the following:
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
end
But, I prefer to create your database in test environment before scenario, as example:
describe "specific name for this scenario" do
before do
#object = create(:object) # using factory girl as example
end
end
so, this data'll be generate for each time you run your test-case, read more about creating data for rspec-test.
I used the advice given in this question: Rspec don't delete 2 specific tables
In spec_helper.rb:
Commented out call to truncate_all_tables.
Then changed line:
config.use_transactional_fixtures = true
to
config.use_transactional_fixtures = false
I found a blog post about Testing Factories First (by BigBinary - which happens to be a Minitest/spec version of Thoughtbot's RSpec original).
Could you please show me the equivalent without the spec framework - just with Minitest (Rails)?
The Thoughtbot approach (RSpec)
spec/factories_spec.rb
FactoryGirl.factories.map(&:name).each do |factory_name|
describe "The #{factory_name} factory" do
it 'is valid' do
build(factory_name).should be_valid
end
end
end
Rakefile
if defined?(RSpec)
desc 'Run factory specs.'
RSpec::Core::RakeTask.new(:factory_specs) do |t|
t.pattern = './spec/factories_spec.rb'
end
end
task spec: :factory_specs
The BigBinary approach (Minitest, spec)
spec/factories_spec.rb
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe FactoryGirl do
EXCEPTIONS = %w(base_address base_batch bad_shipping_address)
FactoryGirl.factories.each do |factory|
next if EXCEPTIONS.include?(factory.name.to_s)
describe "The #{factory.name} factory" do
it 'is valid' do
instance = build(factory.name)
instance.must_be :valid?
end
end
end
end
lib/tasks/factory.rake
desc 'Run factory specs.'
Rake::TestTask.new(:factory_specs) do |t|
t.pattern = './spec/factories_spec.rb'
end
task test: :factory_specs
What is the Minitest equivalent (without spec)?
The approach I am presenting below is slightly different than the two original solutions - in the sense that my approach creates only one test, within which I cycle through the factories and run an assertion against each. I was not able to create a solution that mimics the original solutions any closer - which is (I believe) a separate test method for each factory. If someone could show such an implementation, that would be cool.
test/aaa_factories_tests/factories_test.rb
require File.expand_path(File.dirname(__FILE__) + '/../test_helper.rb')
class FactoriesTest < Minitest::Unit::TestCase
puts "\n*** Factories Test ***\n\n"
EXCEPTIONS = %w(name_of_a_factory_to_skip another_one_to_skip)
def test_factories
FactoryGirl.factories.each do |factory|
next if EXCEPTIONS.include?(factory.name.to_s)
instance = FactoryGirl.build(factory.name)
assert instance.valid?, "invalid factory: #{factory.name}, error messages: #{instance.errors.messages.inspect}"
instance = factory = nil
end
end
end
Thanks to the way Minitest works out of the box -- add any directories under test/ and minitest-rails will automatically create the associated rake task for it. So let's say you add a test/api/ directory, rake minitest:api will automagically be available. -- I see the task when I run bundle exec rake -T with no other configurations:
rake minitest:aaa_factories_tests # Runs tests under test/aaa_factories_tests
And I am able to run this task successfully:
-bash> bundle exec rake minitest:aaa_factories_tests
*** Factories Test ***
Run options: --seed 19208
# Running tests:
.
Finished tests in 0.312244s, 3.2026 tests/s, 9.6079 assertions/s.
1 tests, 3 assertions, 0 failures, 0 errors, 0 skips
Despite the ugliness of prepending the directory with aaa, I am able to have the factories tested first with:
bundle exec rake minitest:all
The reason for the aaa prepend solution is MiniTest does a Dir glob and on Mac OS X (and other Unix variants) the results are sorted alphabetically (though the results differ across different platforms).
As well, I prepended the default_tasks array with aaa_factories_tests to have the factories tested first in the default Minitest task (i.e. when running bundle exec rake minitest).
lib/tasks/factories_first.rake
MiniTest::Rails::Testing.default_tasks.unshift('aaa_factories_tests') if Rails.env =~ /^(development|test)\z/
Note that the above condition avoids erroneously referencing Minitest in environments where it is unavailable (I have confined minitest-rails to :test and :development groups in Gemfile). Without this if-condition, pushing to Heroku (for example to staging or production) will result in uninitialized constant MiniTest.
Of course I am also able to run the factories test directly:
bundle exec ruby -I test test/aaa_factories_tests/factories_test.rb
Here is a solution for MiniTest without the spec framework:
test/factories_test.rb
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
class FactoriesTest < ActiveSupport::TestCase
EXCEPTIONS = %w(griddler_email)
FactoryBot.factories.map(&:name).each do |factory_name|
next if factory_name.to_s.in?(EXCEPTIONS)
context "The #{factory_name} factory" do
should 'be valid' do
factory = build(factory_name)
assert_equal true, factory.valid?, factory.errors.full_messages
end
end
end
end
lib/tasks/factory.rake
namespace :test do
desc 'Test factories'
Rake::TestTask.new(:factories) do |t|
t.pattern = './test/factories_test.rb'
end
end
task minitest: 'test:factories'
The most important thing is to use taks minitest instead of task test if you want the factories tests to be run before other tests.
Whenever I run a user test, RSpec leaves the Fabricated user in the test database after the test has completed, which is messing up my other tests. I will do a rake db:test:prepare, but when I run my tests again, the record is recreated in my database. I have no idea why this is happening. It only happens with user objects.
In my spec_helper file I even have:
config.use_transactional_fixtures = true
Here is an example test that creates a record:
it "creates a password reset token for the user" do
alice = Fabricate(:user)
post :create, email: alice.email
expect(assigns(alice.password_reset_token)).to_not eq(nil)
end
Fabricator:
Fabricator(:user) do
email { Faker::Internet.email }
password 'password'
name { Faker::Name.name }
end
Could this have anything to do with my users model?
you should use a gem called database_cleaner that will truncate your database and reset everything automatically so in your gem file add the gem database_cleaner after that inside your spec_helper.rb configure it
spec_helper.rb
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
and then create a new file in your spec/support directory
spec/support/shared_db_connection.rb
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
Now whenever you run your tests the database will be reset.This was taken from the book 'Everyday Rails testing with RSpec' by Aaron Sumner
Each test is wrapped in a database transaction. That means that everything created during the test should be gone when the test finishes. Therefore, I would suspect that whatever you have in your database was made outside the test itself (like in a before(:all) block).
Also this doesn't guarantee that your database will be empty each time you run your tests. It might be possible that you accidentally added a record somehow, and now it just keeps reverting to that state.
If you want to make sure your tests have a shiny database each time, you should have a look at the database_cleaner gem.
The simplest solution is to make sure RSpec tests run in transactions (Rails does this by default)
spec_helper.rb
config.around(:each) do |example|
ActiveRecord::Base.transaction do
example.run
raise ActiveRecord::Rollback
end
end
If I had to guess, the line post :create, email: alice.email seems like a likely candidate for doing the actual user creation.
Stub that line out with an bogus test and see if you're still getting a user created in the DB.
My problem turned out to be that I was was using before(:all) in my RSpec files which I found out the hard way that the data created does not get rolled back. I switched to before(:example) per this article: https://relishapp.com/rspec/rspec-rails/docs/transactions
I am doing TDD/BDD in Ruby on Rails 3 with Rspec (2.11.0) and FactoryGirl (4.0.0). I have a factory for a Category model:
FactoryGirl.define "Category" do
factory :category do
name "Foo"
end
end
If I drop, create then migrate the database in the test enviroment I get this error:
rake aborted!
Could not find table 'categories'
This problem occurs because FactoryGirl expects the tables to already exist (for some odd reason). If I remove the spec folder from my rails app and do db:migrate, it works. Also if I mark factory-girl-rails from my Gemfile as :require => false it also works (then I have to comment that require in order to run rspec).
I found some information about this problem here: https://github.com/thoughtbot/factory_girl/issues/88
Is there something wrong that I'm doing? How can I "pass by" the FactoryGirl stage in the db:migration task?
I think you need to have factory girl definition like that in Gemfile:
gem 'factory_girl_rails', :require => false
And then you just require it in your spec_helper.rb like that:
require 'factory_girl_rails'
This is the way I'm always using this gem. You don't need to require it in other places than spec_helper.rb. Your current desired approach is just wrong.
A simple fix to this issue is to delay evaluation of any models in your factories by wrapping them in blocks. So, instead of this:
factory :cake do
name "Delicious Cake"
frosting Frosting.new(:flavor => 'chocolate')
filling Filling.new(:flavor => 'red velvet')
end
Do this (notice the curly braces):
factory :cake do
name "Delicious Cake in a box"
frosting { Frosting.new(:flavor => 'chocolate') }
filling { Filling.new(:flavor => 'red velvet') }
end
If you have a lot of factories this may not be feasible, but it is rather straightforward. See also here.
Information from: http://guides.rubyonrails.org/testing.html
When you do end up destroying your testing database (and it will happen, trust me),
you can rebuild it from scratch according to the specs defined in the development
database. You can do this by running rake db:test:prepare.
The rake db:migrate above runs any pending migrations on the development environment
and updates db/schema.rb. The rake db:test:load recreates the test database from the
current db/schema.rb. On subsequent attempts, it is a good idea to first run db:test:prepare, as it first checks for pending migrations and warns you appropriately.
rake db:test:clone Recreate the test database from the current environment’s database schema
rake db:test:clone_structure Recreate the test database from the development structure
rake db:test:load Recreate the test database from the current schema.rb
rake db:test:prepare Check for pending migrations and load the test schema
rake db:test:purge Empty the test database.
You shouldn't need to do any of that.. I think the issue is that your argument to FactoryGirl.define..
try this.
FactoryGirl.define do
factory :category do
name "Foo"
end
end
That should work fine, and does not screw up my migrations or load.. Today, I had to fix an issue where I was referencing a model constant from my factory directly and had to put it in a block to fix things.
FactoryGirl.define do
factory :category do
# this causes unknown table isseus
# state Category::Active
# this does not.
state { Category::Active }
end
end