We run a large app and test times even with spring where around 6s, so I fixed that 🙂
- Preload environment
- Preload test_helper via forking_test_runner
- Disable slow at_exit handler
- Enable by line running via testrbl
- Preload fixtures
- Turn logging on by default
# config/environment.rb Spring::Commands::Test.after_environment if ENV['SPRING_PRELOADED'] # config/spring.rb if defined?(Spring) raise "Spring is out of date: gem install spring" if Gem::Version.new(Spring::VERSION) < Gem::Version.new("1.3.6") ENV['SPRING_LOG'] = 'log/spring.log' # turn logging mode on ENV['SPRING_PRELOADED'] = 'true' class Spring::Commands::Test def env(*) "test" end def call # running by line number ? if ARGV.first =~ /^(\S+):(\d+)$/ file, line = $1, $2 pattern = Testrbl.pattern_from_file(File.readlines(file), line) ARGV[0..0] = [file, "-n", "/#{pattern}/"] end # running with --changed flag ? if ARGV.delete("--changed") ARGV[0...0] = Testrbl.send(:changed_files) end # load all the tests ForkingTestRunner.send(:enable_test_autorun, 'lib/slug_ids') # require an innocent files since we handle require ourselves ARGV.each do |arg| break if arg.start_with?("-") require_test(File.expand_path(arg)) end end def description <<-USAGE Run a test. test/unit/xxx_test.rb:123 # test by line number test/unit # everything _test.rb in a folder (on 1.8 this would be test/unit/*) xxx_test.rb yyy_test.rb # multiple files --changed # run changed tests test/unit/xxx_test.rb -n "/hello/" # test by name USAGE end # we need to do some initialization after everything is loaded # otherwise we end up with for example extra tests running because urls are not cleaned out def self.after_environment return unless Rails.env.test? require 'testrbl' # preload as much of the test environment as possible for fast test startup require "forking_test_runner" ForkingTestRunner.send(:disable_test_autorun) require_relative '../test/helpers/test_helper' if User.count.zero? puts "No fixture data loaded. preload using rake db:fixtures:load" puts "Falling back to slower fixture loading strategy..." else ForkingTestRunner.send(:preload_fixtures) end # make test process not hang 2+s after it is done with tests # minitest calls at_exit twice and we need to hook in after the second # this might lead to some tempfiles pollution or logs missing ... time will tell require 'minitest/unit' class << MiniTest::Unit def at_exit(&block) @at_exit_called ||= 0 @at_exit_called += 1 super if @at_exit_called == 2 super do status = (($!.respond_to?(:status) && $!.status) || 1) exit! status end end end end end private def require_test(path) if File.directory?(path) Dir[File.join path, "**", "*_test.rb"].each { |f| require f } else require path end end end Spring.register_command "test", Spring::Commands::Test.new end