How do I run Rails integration tests without dropping DB contents? - ruby-on-rails

I've written some integration tests that I'd like to run against a copy of my prod database before I push to production. This lets me test all of my routes are still correct, all of the pages render without errors and some of the multipage workflows work as expected.
When I run the integration tests it drops the database I've loaded and loads the test fixtures (as expected). How can I change this behaviour and keep the copy of my production DB I've loaded?

Integration tests calls db:test:prepare which calls db:test:clone_structure which calls db:structure:dump and db:test:purge
You can write your own task
namespace :your_namespace do
Rake::TestTask.new(:integration => "db:migrate(if you want") do |t|
t.libs << "test"
t.pattern = 'test/integration/**/*_test.rb'
t.verbose = true
end
end

To get this to work I had to specifiy the environment when calling the rake task, otherwise it would run the migrations on the development db and then run the tests on the test db; given the example from above
namespace :dbtest do
Rake::TestTask.new(:integration => "db:migrate") do |t|
...
I had to execute the tests like so
rake environment RAILS_ENV=test dbtest:integration

Setting self.use_transactional_fixtures = true in your integration tests would be useful as well if you don't want to have to reload the production copy between each execution of the test.
Otherwise, the integration test run will splat the data with whatever changes it makes.

I needed to add aivarsak's Rake task
namespace :dbtest do
Rake::TestTask.new(:integration) do |t|
t.libs << "test"
t.pattern = 'test/integration/**/*_test.rb'
t.verbose = true
end
end
and also remove the
fixtures :all
line from the test/test_helper.rb file (or create a new one you reference in your integration test files)

Related

Run Rake task programmatically with specified environment

I'm setting up a second database with my Ruby on Rails (3) application, so I want to create a rake task to create the second development database. I'm trying to overwrite the rake db:create task such that it does all the database creation that I need. However, it seems I can't find a suitable way to perform this task. I've tried a few approaches - establishing a connection to the database from the URL:
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
# database.yml contains an entry for secondary_development, this works, as confirmed from rails console
ActiveRecord::Base.establish_connection "postgresql://localhost/secondary_development"
Rake::Task["db:create"].invoke # this does nothing
end
# invoke original db_create task - this works
db_create.invoke
end
end
Another approach was to do:
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
Rails.env = "secondary_development"
Rake::Task["db:create"].invoke
end
# invoke original db_create task - this doesn't work like this
db_create.invoke
end
end
This time only the secondary_development db:create works and the database is created as desired, but the development database is no longer created using this approach.
From one answer I found elsewhere, I thought that reenabling the task would be necessary, but that didn't change anything here and appears not to be the issue.
Finally, an approach that has worked is:
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
system("rake db:create RAILS_ENV=secondary_development")
end
db_create.invoke
end
end
The only issue here is that because the rake task is being run via system, the Rails application has to load before being executed, so I'm essentially loading the application twice fully just to run the task - this will be 3 times when I add a test database into the mix.
So, the actual question(s):
Is it possible to run Rake::Task["..."] programmatically with a specified environment?
Why doesn't ActiveRecord::Base.establish_connection work in this way when creating the database? I had success when running this from Rails console.
I managed to find a solution to this. I believe the reason is that .invoke will not always invoke the task, but it will first determine whether it is necessary. Given that rake db:create is run on several occasions within the same task, .invoke deems the subsequent invocations as unnecessary and therefore does not run them. For the desired behaviour, .execute should be used instead.
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('#tasks').delete('db:create')
namespace :db do
task :create do
if Rails.env == "development"
Rails.env = "secondary_development"
Rake::Task["db:create"].execute # execute rather than invoke
end
# Reset the Rails env to 'development', otherwise it remains as 'secondary_development', which is not what we want (or move this above the if)
Rails.env = "development"
db_create.execute
end
end

Ruby on Rails - run unit tests in subfolder

Is it possible to run tests only from one subfolder?
Something like this:
ruby -I"lib:test" test/functional/api/*
Thanks
I assume you're trying to find a way to run multiple unit tests in a specific directory and you're not using RSpec...
In that case, you can create a task in a Rakefile and run it from command line. This page Rake::TestTask will tell you how to create such a file. You're file is going to look something like this:
Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = FileList['test/functional/api/*.rb']
t.verbose = true
end
And then run:
rake test
If you use rspec you can do this
rspec ./spec/models/
for a folder
and this
rspec ./spec/models/car_spec.rb
for one file
and this
rspec ./spec/models/car_spec.rb:34
for the test on line 34

rake:test not running custom tests in subdirectory

I'm using Rails 4.0.0.beta1. I added two directories: app/services and test/services.
I also added this code, based on reading testing.rake of railties:
namespace :test do
Rake::TestTask.new(services: "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/services/**/*_test.rb'
end
end
I have found that rake test:services runs the tests in test/services; however, rake test does not run those tests. It looks like it should; here is the code:
Rake::TestTask.new(:all) do |t|
t.libs << "test"
t.pattern = "test/**/*_test.rb"
end
Did I overlook something?
Add a line like this after your test task definition:
Rake::Task[:test].enhance { Rake::Task["test:services"].invoke }
I don't know why they're not automatically getting picked up, but this is the only solution I've found that works for Test::Unit.
I think if you were to run rake test:all it would run your additional tests, but rake test alone won't without the snippet above.
For those using a more recent Rails version (4.1.0 in my case)
Use Rails::TestTask instead of Rake::TestTask and override run task:
namespace :test do
task :run => ['test:units', 'test:functionals', 'test:generators', 'test:integration', 'test:services']
Rails::TestTask.new(services: "test:prepare") do |t|
t.pattern = 'test/services/**/*_test.rb'
end
end
Jim's solution works, however it ends up running the extra test suite as a separate task and not as part of the whole (at least using Rails 4.1 it does). So test stats are run twice rather than aggregated. I don't feel this is the desired behaviour here.
This is how I ended up solving this (using Rails 4.1.1)
# Add additional test suite definitions to the default test task here
namespace :test do
Rails::TestTask.new(extras: "test:prepare") do |t|
t.pattern = 'test/extras/**/*_test.rb'
end
end
Rake::Task[:test].enhance ['test:extras']
This results in exactly expected behaviour by simply including the new test:extras task in the set of tasks executed by rake test and of course the default rake. You can use this approach to add any number of new test suites this way.
If you are using Rails 3 I believe just changing to Rake::TestTask will work for you.
Or simply run rake test:all
If you want to run all tests by default, override test task:
namespace :test do
task run: ['test:all']
end

How to drop test and development database in one rake task?

I tried to drop the test and development databases from one rake task like this:
task :regenerate do
Rails.env = "test"
Rake::Task["db:drop"].invoke
Rails.env = "development"
Rake::Task["db:drop"].invoke
end
The test database was dropped successfully. But the development database was not dropped.
Any ideas on how to make this work?
NB: This is on Rails 3.2.3
UPDATE:
Very odd, but reversing the order works:
task :regenerate do
Rails.env = "development"
Rake::Task["db:drop"].invoke
Rails.env = "test"
Rake::Task["db:drop"].invoke
end
What is going on?!
You can write it like this:
namespace :db do
desc "Database custom drop"
task :mydrop do
system("rake db:drop RAILS_ENV=test")
system("rake db:drop RAILS_ENV=development")
end
end
Reversing it does work, because there is some strange code in database_tasks.rb:
def each_current_configuration(environment)
environments = [environment]
environments << 'test' if environment == 'development'
configurations = ActiveRecord::Base.configurations.values_at(*environments)
configurations.compact.each do |configuration|
yield configuration unless configuration['database'].blank?
end
end
It always adds test if env is development. I solved the case of wanting to do a custom db:rebuild task for simultaneous development and test by running development first, and test second. In addition, before running the tasks, I call my set_env method which makes sure to set ActiveRecord::Tasks::DatabaseTasks.env, without this, the database connections don't seem to be handled discretely for environments as expected. I tried all other sorts of disconnect etc, but this worked without further code.
def set_env(env)
Rails.env = env.to_s
ENV['RAILS_ENV'] = env.to_s
ActiveRecord::Tasks::DatabaseTasks.env = env.to_s
end
Here is a gist of my full db.rake file with simultaneous multi-environment db:rebuild and db:truncate
On my system with Ruby 2 and Rails 3.2.13 I can run rake db:drop
This drops both test and development databases. Much easier now than messing with rake tasks

Rails: How to test code in the lib/ directory?

I have a model which gets its data from a parser object. I'm thinking that the parser class should live in the lib/ directory (although I could be persuaded that it should live soewhere else). The question is: Where should my unit tests for the parser class be? And how do I ensure that they are run each time I run rake test?
In the Rails application I'm working on, I decided to just place the tests in the test\unit directory. I will also nest them by module/directory as well, for example:
lib/a.rb => test/unit/a_test.rb
lib/b/c.rb => test/unit/b/c_test.rb
For me, this was the path of last resistance, as these tests ran without having to make any other changes.
Here's one way:
Create lib/tasks/test_lib_dir.rake with the following
namespace :test do
desc "Test lib source"
Rake::TestTask.new(:lib) do |t|
t.libs << "test"
t.pattern = 'test/lib/**/*_test.rb'
t.verbose = true
end
end
Mimic the structure of your lib dir under the test dir, replacing lib code with corresponding tests.
Run rake test:lib to run your lib tests.
If you want all tests to run when you invoke rake test, you could add the following to your new rake file.
lib_task = Rake::Task["test:lib"]
test_task = Rake::Task[:test]
test_task.enhance { lib_task.invoke }
I was looking to do the same thing but with rspec & autospec and it took a little digging to figure out just where they were getting the list of directories / file patterns that dictated which test files to run. Ultimately I found this in lib/tasks/rspec.rake:86
[:models, :controllers, :views, :helpers, :lib, :integration].each do |sub|
desc "Run the code examples in spec/#{sub}"
Spec::Rake::SpecTask.new(sub => spec_prereq) do |t|
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
end
end
I had placed my tests in a new spec/libs directory when the rpsec.rake file was configured to look in spec/lib. Simply renaming libs -> lib did the trick!
An easy and clean way is just to create a directory under test/unit/lib. Then create test as test/unit/lib/foo_test.rb corresponding to lib/foo.rb. No new rake tasks required, and you can nest more directories if needed to match the lib directory structure.
As of Rails 4.0:
rake test:all # Run all tests in any subdir of `test` without resetting the DB
rake test:all:db # Same as above and resets the DB
As of Rails 4.1, redefine test:run to include additional tasks when running rake or rake test:
# lib/tasks/test.rake
namespace :test do
Rake::Task["run"].clear
task run: ["test:units", "test:functionals", "test:generators", "test:integration", "test:tasks"]
["tasks"].each do |name|
Rails::TestTask.new(name => "test:prepare") do |t|
t.pattern = "test/#{name}/**/*_test.rb"
end
end
end
This has the added bonus of defining rake test:tasks in the given example.
As of Rails 4.2, test:run includes all subdirs of test including them when running rake test, and thus rake.
To not define additional rake tasks to run tests from the custom defined folders you may also run them with the command rake test:all. Tests folders structure for the lib folder or any other custom folder is up to you. But I prefer to duplicate them in classes: lib is matched to test/lib, app/form_objects to test/form_objects.
Use:
[spring] rake test:all
to run all tests, including the directories you created (like [root]/test/lib/).
Omit [spring] if tou aren't using it.

Resources