Skip to content

Commit

Permalink
Improve test runner's Minitest integration.
Browse files Browse the repository at this point in the history
This also adds free mix and matching of directories, files and lines filters.
Like so:

bin/rails test models/post_test.rb test/integration models/person_test.rb:26

You can also mix in a traditional Minitest filter:

bin/rails test test/integration -n /check_it_out/
  • Loading branch information
kaspth committed Jun 2, 2015
1 parent 7f60bed commit 1836dc6
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 293 deletions.
10 changes: 10 additions & 0 deletions activesupport/lib/active_support/test_case.rb
Expand Up @@ -9,6 +9,7 @@
require 'active_support/testing/constant_lookup'
require 'active_support/testing/time_helpers'
require 'active_support/testing/file_fixtures'
require 'active_support/testing/composite_filter'
require 'active_support/core_ext/kernel/reporting'

module ActiveSupport
Expand Down Expand Up @@ -38,6 +39,15 @@ def test_order=(new_order)
def test_order
ActiveSupport.test_order ||= :random
end

def run(reporter, options = {})
if options[:patterns] && options[:patterns].any? { |p| p =~ /:\d+/ }
options[:filter] = \
Testing::CompositeFilter.new(self, options[:filter], options[:patterns])
end

super
end
end

alias_method :method_name, :name
Expand Down
54 changes: 54 additions & 0 deletions activesupport/lib/active_support/testing/composite_filter.rb
@@ -0,0 +1,54 @@
require 'method_source'

module ActiveSupport
module Testing
class CompositeFilter # :nodoc:
def initialize(runnable, filter, patterns)
@runnable = runnable
@filters = [ derive_regexp(filter), *derive_line_filters(patterns) ].compact
end

def ===(method)
@filters.any? { |filter| filter === method }
end

private
def derive_regexp(filter)
filter =~ %r%/(.*)/% ? Regexp.new($1) : filter
end

def derive_line_filters(patterns)
patterns.map do |file_and_line|
file, line = file_and_line.split(':')
Filter.new(@runnable, file, line) if file
end
end

class Filter # :nodoc:
def initialize(runnable, file, line)
@runnable, @file = runnable, File.expand_path(file)
@line = line.to_i if line
end

def ===(method)
return unless @runnable.method_defined?(method)

if @line
test_file, test_range = definition_for(@runnable.instance_method(method))
test_file == @file && test_range.include?(@line)
else
@runnable.instance_method(method).source_location.first == @file
end
end

private
def definition_for(method)
file, start_line = method.source_location
end_line = method.source.count("\n") + start_line - 1

return file, start_line..end_line
end
end
end
end
end
4 changes: 2 additions & 2 deletions railties/lib/rails/commands/test.rb
@@ -1,5 +1,5 @@
require "rails/test_unit/runner"
require "rails/test_unit/minitest_plugin"

$: << File.expand_path("../../test", APP_PATH)

Rails::TestRunner.run(ARGV)
exit Minitest.run(ARGV)
6 changes: 5 additions & 1 deletion railties/lib/rails/test_help.rb
Expand Up @@ -3,13 +3,17 @@
abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?

require "rails/test_unit/minitest_plugin"
require 'active_support/testing/autorun'
require 'active_support/test_case'
require 'action_controller'
require 'action_controller/test_case'
require 'action_dispatch/testing/integration'
require 'rails/generators/test_case'

unless Minitest.run_with_rails_extension
Minitest.run_with_autorun = true
require 'active_support/testing/autorun'
end

if defined?(ActiveRecord::Base)
ActiveRecord::Migration.maintain_test_schema!

Expand Down
51 changes: 44 additions & 7 deletions railties/lib/rails/test_unit/minitest_plugin.rb
@@ -1,14 +1,51 @@
require "minitest"
require "active_support/core_ext/module/attribute_accessors"
require "rails/test_unit/reporter"
require "rails/test_unit/test_requirer"

def Minitest.plugin_rails_init(options)
self.reporter << Rails::TestUnitReporter.new(options[:io], options)
if $rails_test_runner && (method = $rails_test_runner.find_method)
options[:filter] = method
module Minitest
def self.plugin_rails_options(opts, options)
opts.separator ""
opts.separator "Usage: bin/rails test [options] [files or directories]"
opts.separator "You can run a single test by appending a line number to a filename:"
opts.separator ""
opts.separator " bin/rails test test/models/user_test.rb:27"
opts.separator ""
opts.separator "You can run multiple files and directories at the same time:"
opts.separator ""
opts.separator " bin/rails test test/controllers test/integration/login_test.rb"
opts.separator ""

opts.separator "Rails options:"
opts.on("-e", "--environment [ENV]",
"Run tests in the ENV environment") do |env|
options[:environment] = env.strip
end

opts.on("-b", "--backtrace",
"Show the complete backtrace") do
options[:full_backtrace] = true
end

options[:patterns] = opts.order!
end

if !($rails_test_runner && $rails_test_runner.show_backtrace?)
Minitest.backtrace_filter = Rails.backtrace_cleaner
def self.plugin_rails_init(options)
self.run_with_rails_extension = true

ENV["RAILS_ENV"] = options[:environment] || "test"

Rails::TestRequirer.require_files options[:patterns] unless run_with_autorun

unless options[:full_backtrace] || ENV["BACKTRACE"]
# Plugin can be run without Rails being loaded, check before filtering.
Minitest.backtrace_filter = Rails.backtrace_cleaner if Rails.respond_to?(:backtrace_cleaner)
end

self.reporter << Rails::TestUnitReporter.new(options[:io], options)
end

mattr_accessor(:run_with_autorun) { false }
mattr_accessor(:run_with_rails_extension) { false }
end

Minitest.extensions << 'rails'
6 changes: 5 additions & 1 deletion railties/lib/rails/test_unit/reporter.rb
Expand Up @@ -15,8 +15,12 @@ def aggregated_results # :nodoc:
filtered_results.reject!(&:skipped?) unless options[:verbose]
filtered_results.map do |result|
location, line = result.method(result.name).source_location
"bin/rails test #{location}:#{line}"
"bin/rails test #{relative_path_for(location)}:#{line}"
end.join "\n"
end

def relative_path_for(file)
file.sub(/^#{Rails.root}\/?/, '')
end
end
end
137 changes: 0 additions & 137 deletions railties/lib/rails/test_unit/runner.rb

This file was deleted.

28 changes: 28 additions & 0 deletions railties/lib/rails/test_unit/test_requirer.rb
@@ -0,0 +1,28 @@
require 'active_support/core_ext/object/blank'
require 'rake/file_list'

module Rails
class TestRequirer # :nodoc:
class << self
def require_files(patterns)
patterns = expand_patterns(patterns)

Rake::FileList[patterns.compact.presence || 'test/**/*_test.rb'].to_a.each do |file|
require File.expand_path(file)
end
end

private
def expand_patterns(patterns)
patterns.map do |arg|
arg = arg.gsub(/:(\d+)?$/, '')
if Dir.exist?(arg)
"#{arg}/**/*_test.rb"
elsif File.file?(arg)
arg
end
end
end
end
end
end
15 changes: 8 additions & 7 deletions railties/lib/rails/test_unit/testing.rake
@@ -1,12 +1,13 @@
require "rails/test_unit/runner"
gem 'minitest'
require 'minitest'
require 'rails/test_unit/minitest_plugin'

task default: :test

desc "Runs all tests in test folder"
task :test do
$: << "test"
args = ARGV[0] == "test" ? ARGV[1..-1] : []
Rails::TestRunner.run(args)
Minitest.run(['test'])
end

namespace :test do
Expand All @@ -23,22 +24,22 @@ namespace :test do
["models", "helpers", "controllers", "mailers", "integration", "jobs"].each do |name|
task name => "test:prepare" do
$: << "test"
Rails::TestRunner.run(["test/#{name}"])
Minitest.run(["test/#{name}"])
end
end

task :generators => "test:prepare" do
$: << "test"
Rails::TestRunner.run(["test/lib/generators"])
Minitest.run(["test/lib/generators"])
end

task :units => "test:prepare" do
$: << "test"
Rails::TestRunner.run(["test/models", "test/helpers", "test/unit"])
Minitest.run(["test/models", "test/helpers", "test/unit"])
end

task :functionals => "test:prepare" do
$: << "test"
Rails::TestRunner.run(["test/controllers", "test/mailers", "test/functional"])
Minitest.run(["test/controllers", "test/mailers", "test/functional"])
end
end

0 comments on commit 1836dc6

Please sign in to comment.