diff options
Diffstat (limited to 'railties')
-rw-r--r-- | railties/CHANGELOG.md | 21 | ||||
-rw-r--r-- | railties/lib/rails/commands.rb | 15 | ||||
-rw-r--r-- | railties/lib/rails/commands/test_runner.rb | 92 | ||||
-rw-r--r-- | railties/test/application/test_runner_test.rb | 183 |
4 files changed, 311 insertions, 0 deletions
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 420ed476b2..6bf9b22a2d 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -19,6 +19,27 @@ *Terence Lee* +* Add `rails test` command to run the test suite + + To run the whole test suite: + + $ rails test + + To run the test file(s): + + $ rails test test/unit/foo_test.rb [test/unit/bar_test.rb ...] + + To run the test suite + + $ rails test [models,helpers,units,controllers,mailers,...] + + For more information, see `rails test --help`. + + This command will eventually replacing `rake test:*`, and `rake test` + command will actually invoking `rails test` instead. + + *Prem Sichanugrist and Chris Toomey* + * Add notice message for destroy action in scaffold generator. *Rahul P. Chaudhari* diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb index aacde52cfc..c76af7d01f 100644 --- a/railties/lib/rails/commands.rb +++ b/railties/lib/rails/commands.rb @@ -5,6 +5,7 @@ aliases = { "d" => "destroy", "c" => "console", "s" => "server", + "t" => "test", "db" => "dbconsole", "r" => "runner" } @@ -16,6 +17,7 @@ The most common rails commands are: generate Generate new code (short-cut alias: "g") console Start the Rails console (short-cut alias: "c") server Start the Rails server (short-cut alias: "s") + test Running the test file (short-cut alias: "t") dbconsole Start a console for the database specified in config/database.yml (short-cut alias: "db") new Create a new Rails application. "rails new my_app" creates a @@ -78,6 +80,19 @@ when 'server' server.start end +when 'test' + $LOAD_PATH.unshift("./test") + require 'rails/commands/test_runner' + if ["-h", "--help"].include?(ARGV.first) + Rails::TestRunner.help_message + exit + else + require APP_PATH + Rails.application.require_environment! + Rails.application.load_tasks + Rails::TestRunner.start(ARGV) + end + when 'dbconsole' require 'rails/commands/dbconsole' Rails::DBConsole.start diff --git a/railties/lib/rails/commands/test_runner.rb b/railties/lib/rails/commands/test_runner.rb new file mode 100644 index 0000000000..a65b508505 --- /dev/null +++ b/railties/lib/rails/commands/test_runner.rb @@ -0,0 +1,92 @@ +require 'optparse' +require 'minitest/unit' + +module Rails + # Handling the all the logic behind +rails test+ command. + class TestRunner + class << self + # Parse the test suite name from the arguments array and pass in a list + # of file to a new +TestRunner+ object, then invoke the evaluation. If + # the argument is not a test suite name, it will be treated as a file + # name and passed to the +TestRunner+ instance right away. + def start(arguments) + case arguments.first + when nil + new(Dir['test/**/*_test.rb']).run + when 'models' + new(Dir['test/models/**/*_test.rb']).run + when 'helpers' + new(Dir['test/helpers/**/*_test.rb']).run + when 'units' + new(Dir['test/{models,helpers,unit}/**/*_test.rb']).run + when 'controllers' + new(Dir['test/controllers/**/*_test.rb']).run + when 'mailers' + new(Dir['test/mailers/**/*_test.rb']).run + when 'functionals' + new(Dir['test/{controllers,mailers,functional}/**/*_test.rb']).run + when 'integration' + new(Dir['test/integration/**/*_test.rb']).run + else + new(arguments).run + end + end + + # Print out the help message which listed all of the test suite names. + def help_message + puts "Usage: rails test [path to test file(s) or test suite type]" + puts "" + puts "Run single test file, or a test suite, under Rails'" + puts "environment. If the file name(s) or suit name is omitted," + puts "Rails will run all the test suites." + puts "" + puts "Support types of test suites:" + puts "-------------------------------------------------------------" + puts "* models (test/models/**/*)" + puts "* helpers (test/helpers/**/*)" + puts "* units (test/{models,helpers,unit}/**/*" + puts "* controllers (test/controllers/**/*)" + puts "* mailers (test/mailers/**/*)" + puts "* functionals (test/{controllers,mailers,functional}/**/*)" + puts "* integration (test/integration/**/*)" + puts "-------------------------------------------------------------" + end + end + + # Create a new +TestRunner+ object with a list of test file paths. + def initialize(files) + @files = files + Rake::Task['test:prepare'].invoke + MiniTest::Unit.output = SilentUntilSyncStream.new(MiniTest::Unit.output) + end + + # Run the test files by evaluate each of them. + def run + @files.each { |filename| load(filename) } + end + + # A null stream object which ignores everything until +sync+ has been set + # to true. This is only to be used to silence unnecessary output from + # MiniTest, as MiniTest calls +output.sync = true+ right before output the + # first test result. + class SilentUntilSyncStream < File + # Create a +SilentUntilSyncStream+ object by given a stream object that + # this stream should set +MiniTest::Unit.output+ to after +sync+ has been + # set to true. + def initialize(target_stream) + @target_stream = target_stream + super(File::NULL, 'w') + end + + # Swap +MiniTest::Unit.output+ to another stream when +sync+ is true. + def sync=(sync) + if sync + @target_stream.sync = true + MiniTest::Unit.output = @target_stream + end + + super + end + end + end +end diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb new file mode 100644 index 0000000000..49bce508ee --- /dev/null +++ b/railties/test/application/test_runner_test.rb @@ -0,0 +1,183 @@ +require 'isolation/abstract_unit' + +module ApplicationTests + class TestRunnerTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + create_schema + end + + def teardown + teardown_app + end + + def test_should_not_display_heading + create_test_file + run_test_command.tap do |output| + assert_no_match /Run options:/, output + assert_no_match /Running tests:/, output + end + end + + def test_run_shortcut + create_test_file :models, 'foo' + output = Dir.chdir(app_path) { `bundle exec rails t test/models/foo_test.rb` } + assert_match /1 tests, 1 assertions, 0 failures/, output + end + + def test_run_single_file + create_test_file :models, 'foo' + assert_match /1 tests, 1 assertions, 0 failures/, run_test_command("test/models/foo_test.rb") + end + + def test_run_multiple_files + create_test_file :models, 'foo' + create_test_file :models, 'bar' + assert_match /2 tests, 2 assertions, 0 failures/, run_test_command("test/models/foo_test.rb test/models/bar_test.rb") + end + + def test_run_file_with_syntax_error + app_file 'test/models/error_test.rb', <<-RUBY + require 'test_helper' + def; end + RUBY + + error_stream = Tempfile.new('error') + redirect_stderr(error_stream) { run_test_command('test/models/error_test.rb') } + assert_match /SyntaxError/, error_stream.read + end + + def test_invoke_rake_test_prepare + app_file "lib/tasks/test.rake", <<-RUBY + namespace :test do + task :prepare do + puts "Hello World" + end + end + RUBY + create_test_file + assert_match /Hello World/, run_test_command + end + + def test_run_models + create_test_file :models, 'foo' + create_test_file :models, 'bar' + create_test_file :controllers, 'foobar_controller' + run_test_command("models").tap do |output| + assert_match /FooTest/, output + assert_match /BarTest/, output + assert_match /2 tests, 2 assertions, 0 failures/, output + end + end + + def test_run_helpers + create_test_file :helpers, 'foo_helper' + create_test_file :helpers, 'bar_helper' + create_test_file :controllers, 'foobar_controller' + run_test_command('helpers').tap do |output| + assert_match /FooHelperTest/, output + assert_match /BarHelperTest/, output + assert_match /2 tests, 2 assertions, 0 failures/, output + end + end + + def test_run_units + create_test_file :models, 'foo' + create_test_file :helpers, 'bar_helper' + create_test_file :unit, 'baz_unit' + create_test_file :controllers, 'foobar_controller' + run_test_command('units').tap do |output| + assert_match /FooTest/, output + assert_match /BarHelperTest/, output + assert_match /BazUnitTest/, output + assert_match /3 tests, 3 assertions, 0 failures/, output + end + end + + def test_run_controllers + create_test_file :controllers, 'foo_controller' + create_test_file :controllers, 'bar_controller' + create_test_file :models, 'foo' + run_test_command('controllers').tap do |output| + assert_match /FooControllerTest/, output + assert_match /BarControllerTest/, output + assert_match /2 tests, 2 assertions, 0 failures/, output + end + end + + def test_run_mailers + create_test_file :mailers, 'foo_mailer' + create_test_file :mailers, 'bar_mailer' + create_test_file :models, 'foo' + run_test_command('mailers').tap do |output| + assert_match /FooMailerTest/, output + assert_match /BarMailerTest/, output + assert_match /2 tests, 2 assertions, 0 failures/, output + end + end + + def test_run_functionals + create_test_file :mailers, 'foo_mailer' + create_test_file :controllers, 'bar_controller' + create_test_file :functional, 'baz_functional' + create_test_file :models, 'foo' + run_test_command('functionals').tap do |output| + assert_match /FooMailerTest/, output + assert_match /BarControllerTest/, output + assert_match /BazFunctionalTest/, output + assert_match /3 tests, 3 assertions, 0 failures/, output + end + end + + def test_run_integration + create_test_file :integration, 'foo_integration' + create_test_file :models, 'foo' + run_test_command('integration').tap do |output| + assert_match /FooIntegration/, output + assert_match /1 tests, 1 assertions, 0 failures/, output + end + end + + def test_run_whole_suite + types = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration] + types.each { |type| create_test_file type, "foo_#{type}" } + run_test_command('') .tap do |output| + types.each { |type| assert_match /Foo#{type.to_s.camelize}Test/, output } + assert_match /7 tests, 7 assertions, 0 failures/, output + end + end + + private + def run_test_command(arguments = 'test/unit/test_test.rb') + Dir.chdir(app_path) { `bundle exec rails test #{arguments}` } + end + + def create_schema + app_file 'db/schema.rb', '' + end + + def redirect_stderr(target_stream) + previous_stderr = STDERR.dup + $stderr.reopen(target_stream) + yield + target_stream.rewind + ensure + $stderr = previous_stderr + end + + def create_test_file(path = :unit, name = 'test') + app_file "test/#{path}/#{name}_test.rb", <<-RUBY + require 'test_helper' + + class #{name.camelize}Test < ActiveSupport::TestCase + def test_truth + puts "#{name.camelize}Test" + assert true + end + end + RUBY + end + end +end |