🐒

テストメソッドを1つだけ実行するのが面倒だったので minitest_runner.rb というスクリプトを書いてみた

2021/04/17に公開

minitest では次のように -n オプションを使うことでテストメソッド(テストケース)1個だけを実行することができます。

ruby test/test_foo.rb -n {メソッド名}

このときファイル名はシェルの補完が効くのでスッと入力できますが、メソッド名はシェルの補完が効きません。

で、手入力したり、エディタで開いているテストコードやファイルを grep した結果からコピペしてくることになるのですが、いずれにしてもめんどくさく……。

というわけでやっつけスクリプトを書いてみました。

使い方

  • (1) プロジェクトのルートディレクトリで ruby minitest_runner.rb を実行する
  • (2) テストファイルが番号付きで列挙されるので、番号を入力して選択
  • (3) 選択したファイル内のテストメソッドが番号付きで列挙されるので、番号を入力して選択
  • (4) 選択したテストメソッドが実行される

というのが基本ですが、

ruby minitest_runner.rb {ファイルの番号} {メソッドの番号}

のように引数でファイルとメソッドの番号を指定した場合は非対話的に実行されるようにしています。すでに番号が分かっていて何度も繰り返したい場合はこれで。

実行の例

例として Rake でやってみたもの。

$ ruby minitest_runner.rb
----
files:
  (0) . / test / test_private_reader.rb (0)
  (1) . / test / test_rake.rb (1)
  (2) . / test / test_rake_application.rb (2)
  ... 長いので途中省略 ...
  (43) . / test / test_rake_win32.rb (43)
  (44) . / test / test_thread_history_display.rb (44)
  (45) . / test / test_trace_output.rb (45)

Select item: 44 ... 番号を入力する
----
methods:
  (0) test_banner (0)
  (1) test_item_queued (1)
  (2) test_item_dequeued (2)
  (3) test_multiple_items (3)
  (4) test_waiting (4)
  (5) test_continue (5)
  (6) test_thread_deleted (6)
  (7) test_thread_created (7)

Select item: 6 ... 番号を入力する
----
bundle exec ruby ./test/test_thread_history_display.rb -n test_thread_deleted ... 実際に実行されるコマンド
----
Run options: -n test_thread_deleted --seed 62390

# Running:

.

Finished in 0.001000s, 999.5052 runs/s, 1999.0104 assertions/s.

1 runs, 2 assertions, 0 failures, 0 errors, 0 skips

ソース

10分くらいで適当に書いたものなのでプロジェクトによっては動かせなかったりすると思いますが、必要になったらその都度いじればいいかなと。

# minitest_runner.rb

def select_item(title, items)
  puts "----"
  puts "#{title}:"

  # 候補一覧を表示
  items
    .map { |item| item.gsub("/", " / ") } # パスの区切りを見やすく
    .each_with_index { |item, i| puts "  (#{i}) #{item} (#{i})" }

  # ユーザの入力を受け取る
  puts ""
  print "Select item: "
  input = $stdin.gets

  raise if /\d+/ !~ input

  items[input.to_i]
end

def select_file(idx)
  files = Dir.glob("./test/**/test_*.rb").sort

  if idx
    files[idx.to_i]
  else
    select_item("files", files)
  end
end

def select_method(file, idx)
  method_lines =
    File.read(file).each_line
      .select { |line| /def test_.+/ =~ line }
      .map { |line| line.sub(/def /, "").strip }

  method_line =
    if idx
      method_lines[idx.to_i]
    else
      select_item("methods", method_lines)
    end

  md = method_line.match(/(test_[a-z0-9_]+)/)
  md[1]
end

# --------------------------------

file_idx, method_idx = ARGV

file = select_file(file_idx)
method = select_method(file, method_idx)

cmd = ""
cmd << "bundle exec " if File.exist?("./Gemfile")
cmd << "ruby #{file} -n #{method}"

puts "----"
puts cmd
puts "----"

system cmd
exit $?.exitstatus unless $?.success?

Ruby関連で他に書いたもの

https://memo88.hatenablog.com/archive/category/Ruby

Discussion