I'm using Rspec to test my code against a legacy database (no migration, no schema etc). Is there a way to prevent the database from being dropped between iterations? Thanks.
config.before(:suite) do
DatabaseCleaner.strategy = nil
end
Try the https://github.com/bmabey/database_cleaner gem.
DatabaseCleaner.strategy = nil # will not do any db cleaning
I hope you can test on a local db before trying it on the real legacy db!
Related
I'm writing a gem for catching flaky tests. The idea is to dump database to locate randomized factory. I'm using system call in the .after hook:
config.after do |example|
system "pg_dump -U postgres -d #{db_name} > #{path}/#{status}.sql"
end
At this moment database contains a record - i've made sure of that by byebug.
However i bump into a problem - as i see, transaction is not closed, so if i try get record via psql - i get nothing, record doesn't exist.
Any ideas how to close transaction during spec? Or maybe i'm wrong somewhere deeply?
Yes, the matter is the transaction was not closed. Database cleaner wrap all rspec execution in begin-rollback. I found two ways to solve my problem:
Use active record connection to dump database content, but it's no so easy - you have to use COPY command to copy (unexpected, huh?) ALL tables to file (.csv for example) and then restore them. Ew, tedious.
Change database cleaner strategie from :transaction to :truncation:
config.before(:each, flaky: true) do
DatabaseCleaner.strategy = :truncation
end
That's the solution.
I am having trouble in cleaning database between features.
I tried using Before hooks but it runs for each scenario but I only need to clean database at the start of each feature and not between scenarios.
Any suggestions would be helpful.
I use DatabaseCleaner https://github.com/DatabaseCleaner/database_cleaner I'm satisfed
config.before(:each) do |spec|
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.start
...
end
config.append_after(:each) do
DatabaseCleaner.clean
end
in rails_herper.rb
Here is sample config
before(:all) do
DatabaseCleaner.clean
end
In RSpec, you have tags, types, hooks etc..
In your case easiest way will be adding before(: all) in each file.
This will start cleaning before all tests in described context.
From documentation Rspec Docs
before(:all) blocks are run once before all of the examples in a group
The database should be cleaned before every scenario as Cucumber intends. Stopping Cucumber from doing this is a false optimisation, and a common anti-pattern followed by lots of less experienced Cucumber users. Scenarios should never be dependent on each other.
To get this to work remove any code you have added to your application to restrict how cucumber cleans the database.
If you are not sure how to do this create a new rails project using the same ruby and rails versions you are using and then add the cucumber-rails gem. It will setup everything as intended. You can use the diff of before/after cucumber-rails to compare.
You can clean database before (not after) each scenario with the following code. Just add it to your features/support/env.rb
Cucumber::Rails::Database.autorun_database_cleaner = false
DatabaseCleaner.strategy = :truncation
Cucumber::Rails::Database.javascript_strategy = :truncation
Before do
DatabaseCleaner.clean
end
Just a work around/hack, just in case you haven't found a solution yet.
The trick here is to use tagged cucumber hooks!
Provide a tag like #LastScenario in your last scenario in the feature file/s.
Then using the #After hook of cucumber perform the action, say data clean up in your case.
Something like:
#LastScenario
Scenario: My Scenario Name
Given I have something...
And then in the Hooks.java Class:
public class Hooks {
#After("#LastScenario")
public void dataCleanUp() {
CleanUpScripts cleanUpScripts = new CleanUpScripts();
cleanUpScripts.dataCleanUp();
}
}
Same can be done using #Before Hook as well - based on what is required.
I've recently started testing my app using RSpec. Being the testing noob that i am, i did not install the full suite of tools that people normally use (namely FactoryGirl, also now known as FactoryBot. And DatabaseCleaner).
Anyway here's what happened. After getting my feet wet with RSpec, i started using FactoryBot so that my tests looks less convoluted. Note that :user is a Devise object.
# spec/models/first_test_spec.rb
FactoryBot.create(:user)
#Other Code
So i'm doing stuff with my first_test_spec.rb and i run it and everything is working perfectly fine. I then create my 2nd spec file
# spec/models/second_test_spec.rb
FactoryBot.create(:user)
#Other Code
Now here comes the problem. My tests are failing because FactoryBot.create(:user) is invalid, due to Devise requiring unique emails. Clearly this indicates that the data from my first_test_spec is persisting hence the error.
So i attempt to install DatabaseCleaner and hopefully clear my Test DB after each run. My rails_helper looks like this:
require 'spec_helper'
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 'rspec/rails'
require 'support/factory_bot'
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
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
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
I think i've got everything set up correctly, so i'm uncertain if the errors are still occurring due to my DatabaseCleaner being set up wrongly.
Anyway, running rspec still throws the same error, where second_test_spec cannot create the user object because the email already exists (a validation that comes with Devise itself).
I then proceed to run the following:
rake db:test:prepare
rspec spec/models/second_test_spec.rb
And it still throws the same error. So right now, i have no idea if my database is being cleanse after running rspec. But i'm quite certain that i have been unable to purge my test database.
So i guess i really have 2 questions/problems:
1) How do you purge your test database? (Googling reveals that rake db:test:prepare is the way)
2) Is my DatabaseCleaner setup correctly? If so, shouldn't it be purging the database?
UPDATE:
As suggested to me in the comments, using sequence for creating unique fields with FactoryBot is recommended. Obviously that made the problem go away because there would no longer be validation errors from Devise.
I then went on to test a couple of things.
rails c test
I ran this to check my test database and it was indeed empty. This indicates that DatabaseCleaner is working perfectly fine. What i fail to understand then, is why the need to sequence my email creation in FactoryBot? Or i suppose, i fail to understand how does RSpec "compile & run".
puts #user.email
So i wanted to print out the emails to look at the sequencing to see if i'm able to decipher the problem. Here's what happens:
running rspec spec/models/first_test_spec.rb
Tutor email yields the number 2.
running rspec spec/models/second_test_spec.rb
Tutor email yields the number 3.
running rspec
Tutor email yields the numbers 2 & 5.
So i'm not sure if there are "problems" with my test suite. Clearly my original problem has been fixed, and this is a separate topic altogether. But i figured if anyone would like to explain this mystery to anyone else who chances upon this thread may wish to do so.
Seeing your full spec files would help. If you are running the specs as you've written - creating a user outside of an example group or a before block - that will cause records to be written outside of the cleaning strategy scope and can cause data to remain across examples. Your DBcleaner config looks to be set up fine otherwise.
rake db:test:prepare is the correct way to clean out your test db but shouldn't need to be ran often unless you have migration changes. You can jump into a rails console within the test environment rails c test and look around to see if there are any records left behind.
As a side note you can flip config.use_transactional_fixtures from false to true and remove DBcleaner altogether from your app. Just make sure there is no residual data in your database before going this route.
I've 2 _spec files: the first containing unit test, the other one some integration tests.
While running them by specifying file name they are alle green, while running them as "rspec" 3 of them are failing.
I'm using database_cleaner gem to clean test db around each test.
config.use_transactional_fixtures = false
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
I'm using "let" to instantiate objects and FactoryGirl to create sample objects.
Has anyone experienced something like this before?
Some issues could be:
Class variables that you are modifying and not resetting between tests. Class variables are anti-patterns and should generally be avoided.
You aren't properly stubbing things like constants.
Anything that maintains a state between tests can cause the issue you are seeing. Use your debugger to check the state of objects during each test.
I've solved my problem by moving from def to let. Be aware of defines
My problems:
The customer database is in a machine over a VPN, so my tests are very slow (about 30-40 seconds each)
This database is not entirely for my application and my data are related to the data that was already on the database
Composite primary keys are present on the old database tables and the informix adapter does not support it for fixtures
What I'm using:
Ruby on Rails 4.2.5
Jruby 9.0.1.0
Informix database via JDBC connection
Minitest
Minitest-reportes
What I did so far:
The test time was decreased in 5-10 seconds with Devise.stretches = 1 and Rails.logger.level = 4 configs.
I can't use fixture for the data that have relations with the tables that was in the system and have composite primary keys, so I built my own fixture methods for this tables, removing and creating objects at the first use and caching them in global variables after create to can be used without go to the database on next tests. This data I was inserting almost every test, so with this I could decrease the time a little more.
Apparently rails fixtures was deleting and creating them all on every test, so I tried to disable use of transactional fixtures (self.use_transactional_fixtures = false) but apparently this does not change a thing.
I need to reset "my fixtures" before the test suite and unfortunately rails fixtures are depending on them and when I'm trying to delete my data I'm getting some errors. Tried to drop "my fixtures" before creating rails fixtures with this code:
class ActiveRecord::FixtureSet
class << self
alias :orig_create_fixtures :create_fixtures
end
def self.create_fixtures f_dir, fs_names, *args
reset_cache
#Deleting my fixture data
People.delete_all
orig_create_fixtures f_dir, fs_names, *args
end
end
The problem with the above code is delete_all is being executed on each test.
Besides the errors, I'm getting with all this fixture stuff, I reduced my tests time to about 8 seconds each.
My test helper:
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require "rails/test_help"
require 'minitest/autorun'
require 'minitest/reporters'
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
# Load support files
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
ActiveSupport::TestCase.class_eval do
extend MiniTest::Spec::DSL
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
self.use_transactional_fixtures = false
Devise.stretches = 1
Rails.logger.level = 4
reporter_options = {color: true, slow_count: 5}
Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(reporter_options)]
end
Can I do something better? Any clues to solve this problem with removing data? Any way to improve a little more my tests performance?