4

I'm trying to create a rake task for a Rails 4.0.2 (Ruby 2.2.3) project that creates the test database along with seeding, then runs the test suite, and finally cleanups afterwards by dropping the test database. Here's a simple example below:

task better_test: :environment do
  Rake::Task["test:setup"].execute
  Rake::Task["test"].execute
  Rake::Task["test:cleanup"].execute
end

The task above does invoke the test:setup rake task (create/seed the test database), then invokes the test rake task, and finally invokes the test:cleanup rake task. The problem however is that the last test:cleanup is invoked before the test rake task is finished running. Is there anyway to invoke the cleanup rake task after the previous tasks are complete?

Here's the output from running the task with trace:

$ RAILS_ENV=test be rake better_test --trace
/usr/local/rvm/gems/ruby-2.2.3/gems/activesupport-4.0.2/lib/active_support/values/time_zone.rb:282: warning: circular argument reference - now
/usr/local/rvm/gems/ruby-2.2.3/gems/honeybadger-1.16.3/lib/honeybadger/rack/user_feedback.rb:51: warning: circular argument reference - action
** Invoke better_test (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute better_test
** Execute test:setup
Creating test database.
** Execute db:test:setup
** Execute db:test:create
** Execute db:create
** Execute db:test:load
** Execute db:schema:load
** Execute db:test_project:setup
** Execute db:test_project:create
** Invoke db:create (first_time)
** Invoke environment
** Execute db:create
** Execute db:test_project:migrate:down
** Execute db:test_project:migrate:up
** Execute db:test_project:seed
** Execute test
** Invoke test:run (first_time)
** Invoke test:units (first_time)
** Invoke test:prepare (first_time)
** Invoke db:test:prepare (first_time)
** Invoke db:abort_if_pending_migrations (first_time)
** Invoke environment
** Invoke db:migrate:load (first_time)
** Invoke environment
** Execute db:migrate:load
** Execute db:abort_if_pending_migrations
** Execute db:test:prepare
** Execute db:drop
** Execute db:create
** Execute db:load
** Invoke db:schema:load (first_time)
** Invoke environment
** Execute db:schema:load
** Execute test:prepare
** Execute test:units
** Invoke test:functionals (first_time)
** Invoke test:prepare
** Execute test:functionals
** Invoke test:integration (first_time)
** Invoke test:prepare
** Execute test:integration
** Execute test:run
** Invoke test:decorators (first_time)
** Invoke test:prepare
** Execute test:decorators
** Execute test:cleanup
** Execute db:test:drop
** Execute db:drop
** Execute db:test_project:drop
** Invoke db:drop (first_time)
** Invoke environment
** Execute db:drop
Run options: --seed 19360

# Running tests:

EE....

Finished tests in 0.037493s, 160.0278 tests/s, 106.6852 assertions/s.

As you can see the tasks are invoked in order but the cleanup task is executed before the tests have completed. Any ideas to address this?

Tyler Amos
  • 101
  • 7

3 Answers3

4

As it turns out, this was less to do with rake and more to do with the way Minitest executes its tests. I mentioned in the question it appeared that the rake tasks were being invoked in the correct order but the tests were executed later for some reason. The reason is because Minitest utilizes the ruby Kernel method at_exit for execution of the tests. The test files are read in at the time the rake test was invoked but all tests are executed at the end of the ruby program, even after my subsequent rake tasks. Here's a blog article that explains the issue in more detail: http://blog.arkency.com/2013/06/are-we-abusing-at-exit/.

Tyler Amos
  • 101
  • 7
2

Try to use the rake standard syntax for calling multiple tasks:

task better_test: ["test:setup", "test", "test:cleanup"]

Maybe this will resolve you problem.

Also, it seems that Rake actually builds the list of all tasks it should run before executing them, and run each one only once. For example:

task :a
task :b
task :c

task better_test: ["a", "b", "c", "b"]

results in:

$ rake better_test -t
** Invoke better_test (first_time)
** Invoke a (first_time)
** Execute a
** Invoke b (first_time)
** Execute b
** Invoke c (first_time)
** Execute c
** Execute better_test

As you see, the task b is run only once (the first one). I believe that your test:setup or test somehow depend on test:cleanup task. So it is called earlier than expected.

Dmitry Sokurenko
  • 6,042
  • 4
  • 32
  • 53
  • Dmitry, that's a good thought, but same result. I really feel like it has less to do with rake and more to do with how minitest is executing tests. – Tyler Amos Nov 19 '15 at 22:08
  • I've added some other idea into the answer, probably your `test:setup` or `test` already call the cleanup (not sure why, but maybe you or some gem add a dependency to run the `cleanup`), so it is not called the second time at all. – Dmitry Sokurenko Nov 19 '15 at 22:21
0
Rake::Task["test"].enhance do
  Rake::Task["test:cleanup"].invoke
end
Marcin Urbanski
  • 2,493
  • 1
  • 16
  • 17
  • 1
    Thanks Marcin. I've tried that route too, but the tests still run after the cleanup task is executed. They are invoked in order, but the second task does not wait for the first one to complete. – Tyler Amos Nov 19 '15 at 22:03