Skip to content

Commit

Permalink
Run all tests by default; ability to run tests by arbitrary path(s)
Browse files Browse the repository at this point in the history
Similar to RSpec and the Rails test runner, this commit adds a feature
where the `mt` executable can run multiple tests at once based on
command line arguments. When executed with no args, it now runs all
tests by default.

```sh
# Run all tests
bin/mt

# Run a specific test file
bin/mt test/cli_test.rb

# Run a directory of tests
bin/mt test/commands

# Run many tests/directories at once
bin/mt test/cli_test.rb test/commands
```
  • Loading branch information
mattbrictson committed Feb 21, 2024
1 parent 5d80649 commit d497975
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 12 deletions.
30 changes: 25 additions & 5 deletions lib/mighty_test/cli.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module MightyTest
class CLI
def initialize(env: ENV, option_parser: OptionParser.new, runner: MinitestRunner.new)
def initialize(file_system: FileSystem.new, env: ENV, option_parser: OptionParser.new, runner: MinitestRunner.new)
@file_system = file_system
@env = env.to_h
@option_parser = option_parser
@runner = runner
Expand All @@ -26,7 +27,7 @@ def run(argv: ARGV)

private

attr_reader :env, :path_args, :extra_args, :options, :option_parser, :runner
attr_reader :file_system, :env, :path_args, :extra_args, :options, :option_parser, :runner

def print_help
# Minitest already prints the `-h, --help` option, so omit mighty_test's
Expand All @@ -44,14 +45,33 @@ def run_test_by_line_number
test_name = TestParser.new(path).test_name_at_line(line.to_i)

if test_name
runner.run_inline_and_exit!(path, args: ["-n", "/^#{Regexp.quote(test_name)}$/"] + extra_args)
run_tests_and_exit!(path, flags: ["-n", "/^#{Regexp.quote(test_name)}$/"])
else
runner.run_inline_and_exit!(args: extra_args)
run_tests_and_exit!
end
end

def run_tests_by_path
runner.run_inline_and_exit!(*path_args, args: extra_args)
test_paths = find_test_paths
run_tests_and_exit!(*test_paths)
end

def find_test_paths
return file_system.find_test_paths if path_args.empty?

path_args.flat_map do |path|
if Dir.exist?(path)
file_system.find_test_paths(path)
elsif File.exist?(path)
[path]
else
raise ArgumentError, "#{path} does not exist"
end
end
end

def run_tests_and_exit!(*test_paths, flags: [])
runner.run_inline_and_exit!(*test_paths, args: extra_args + flags)
end

def handle_exception(e) # rubocop:disable Naming/MethodParameterName
Expand Down
5 changes: 5 additions & 0 deletions lib/mighty_test/file_system.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@ def find_matching_test_file(path)
test_path = path[%r{^(?:app|lib)/(.+)\.[^\.]+$}, 1].then { "test/#{_1}_test.rb" }
test_path if test_path && File.exist?(test_path)
end

def find_test_paths(directory="test")
glob = File.join(directory, "**/*_test.rb")
Dir[glob]
end
end
end
3 changes: 2 additions & 1 deletion lib/mighty_test/option_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ def initialize
@parser = ::OptionParser.new do |op|
op.require_exact = true
op.banner = <<~BANNER
Usage: mt <test file>...
Usage: mt
mt [test file...] [test dir...]
mt --watch
BANNER
Expand Down
Empty file.
Empty file.
Empty file.
93 changes: 87 additions & 6 deletions test/mighty_test/cli_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

module MightyTest
class CLITest < Minitest::Test
include FixturesPath

def test_help_flag_prints_usage_and_minitest_options
result = cli_run(argv: ["--help"])

Expand All @@ -27,15 +29,96 @@ def test_version_flag_prints_version
assert_equal(VERSION, result.stdout.chomp)
end

def test_with_no_args_runs_all_tests_in_the_test_directory
with_fake_minitest_runner do |runner, executed_tests|
cli_run(argv: [], chdir: fixtures_path.join("rails_project"), runner:)

assert_equal(
%w[
test/helpers/users_helper_test.rb
test/models/account_test.rb
test/models/user_test.rb
test/system/users_system_test.rb
],
executed_tests.sort
)
end
end

def test_with_a_directory_arg_runs_all_test_files_in_that_directory
with_fake_minitest_runner do |runner, executed_tests|
cli_run(argv: ["test/models"], chdir: fixtures_path.join("rails_project"), runner:)

assert_equal(
%w[
test/models/account_test.rb
test/models/user_test.rb
],
executed_tests.sort
)
end
end

def test_with_a_mixture_of_file_and_directory_args_runs_all_matching_tests
with_fake_minitest_runner do |runner, executed_tests|
cli_run(argv: %w[test/system test/models/user_test.rb], chdir: fixtures_path.join("rails_project"), runner:)

assert_equal(
%w[
test/models/user_test.rb
test/system/users_system_test.rb
],
executed_tests.sort
)
end
end

def test_with_explict_file_args_runs_those_files_regardless_of_whether_they_appear_to_be_tests
with_fake_minitest_runner do |runner, executed_tests|
cli_run(argv: ["app/models/user.rb"], chdir: fixtures_path.join("rails_project"), runner:)

assert_equal(
%w[
app/models/user.rb
],
executed_tests.sort
)
end
end

def test_with_directory_args_only_runs_files_that_appear_to_be_tests
with_fake_minitest_runner do |runner, executed_tests|
cli_run(argv: ["app/models"], chdir: fixtures_path.join("rails_project"), runner:)

assert_empty(executed_tests)
end
end

def test_with_non_existent_path_raises_an_error
error = assert_raises(ArgumentError) do
cli_run(argv: ["test/models/non_existent_test.rb"], chdir: fixtures_path.join("rails_project"))
end

assert_includes(error.message, "test/models/non_existent_test.rb does not exist")
end

private

def cli_run(argv:, env: {}, stdin: nil, raise_on_failure: true)
def with_fake_minitest_runner
executed_tests = []
runner = MinitestRunner.new
runner.stub(:run_inline_and_exit!, ->(*test_files, **) { executed_tests.append(*test_files.flatten) }) do
yield(runner, executed_tests)
end
end

def cli_run(argv:, env: {}, chdir: ".", runner: nil, raise_on_failure: true)
exitstatus = true
orig_stdin = $stdin
$stdin = StringIO.new(stdin) if stdin

stdout, stderr = capture_io do
CLI.new(env:).run(argv:)
Dir.chdir(chdir) do
CLI.new(**{ env:, runner: }.compact).run(argv:)
end
rescue SystemExit => e
exitstatus = e.status
end
Expand All @@ -44,8 +127,6 @@ def cli_run(argv:, env: {}, stdin: nil, raise_on_failure: true)
raise "CLI exited with status: #{exitstatus}" if raise_on_failure && result.failure?

result
ensure
$stdin = orig_stdin
end
end
end

0 comments on commit d497975

Please sign in to comment.