test-unitさえあればご飯大盛り三杯はイケる

rspec3 に疲弊している皆さんこんにちは。今日は rspec3 に疲れた皆さんへ憩いの場として test-unit gem のお話を提供したいと思います。

test-unit gem について

Rubyのテスティングフレームワークの歴史(2014年版)」の記事に詳しく書いてありますが、test-unit gem は古来にあった test/unit とも minitest とも違う第3の(第4?5?よくわかりません)の単体テストライブラリです。

Ruby コミッタでもある @kou 氏が精力的にメンテナンスを続けてくれており、 Ruby 2.2 に標準で bundle される今をときめくテスティングライブラリです。

古来の test/unit に比べて多くの機能が入っており、大変便利になっています。

テストの書き方

本来ならばここで、テストの記法について語るのでしょうが、@repeatedly 氏が以前に書いてくれた 「Test::Unit でテストを書く」の記事が大変参考になるので、そちらにおまかせします。

本記事では、それに対する補完として、test-unit のコマンドラインオプションについて記載します。

ファイルを指定してテストを実行する。

rspec では、以下のようにファイルを指定してテストを実行できました。

rspec spec/xxx_spec.rb

これは test-unit でもできます。require 'test/unit' しているファイルを直接指定して、ruby スクリプトとして実行すれば OK です。

# test/test_xxx.rb
require 'test/unit'
class TestXXX < Test::Unit::TestCase
end
$ ruby test/test_xxx.rb

ちなみに、require してクラス定義しているだけなのに、なんでスクリプト実行されるの?メソッド呼んでないよ?という気になりますが、どうやら以下のような挙動になっているらしいです。

上の例では、テストクラスを「定義しただけ」で、テストが実行されています。 これは、require 'test/unit'した時に Test::Unit.autorunを実行し ているためです。その結果、終了時の後処理として実行されるようになってい ます。

cf. http://docs.ruby-lang.org/ja/2.1.0/library/test=2funit.html (正確には見るべきドキュメントが違いますが似たような挙動でしょう)

ヘルプ

-h オプションでヘルプを参照できます。test-unit には rspec のようなコマンドはないので、これも require 'unit/test' しているファイルを指定して、-h オプションを指定します。

ruby test/test_xxx.rb -h

とりあえずヘルプを全部貼っておきます。

Test::Unit automatic runner.
Usage: test_xxx.rb [options] [-- untouched arguments]
    -r, --runner=RUNNER              Use the given RUNNER.
                                     (c[onsole], e[macs], x[ml])
        --collector=COLLECTOR        Use the given COLLECTOR.
                                     (de[scendant], di[r], l[oad], o[bject]_space)
    -n, --name=NAME                  Runs tests matching NAME.
                                     (patterns may be used).
        --ignore-name=NAME           Ignores tests matching NAME.
                                     (patterns may be used).
    -t, --testcase=TESTCASE          Runs tests in TestCases matching TESTCASE.
                                     (patterns may be used).
        --ignore-testcase=TESTCASE   Ignores tests in TestCases matching TESTCASE.
                                     (patterns may be used).
        --location=LOCATION          Runs tests that defined in LOCATION.
                                     LOCATION is one of PATH:LINE, PATH or LINE
        --attribute=EXPRESSION       Runs tests that matches EXPRESSION.
                                     EXPRESSION is evaluated as Ruby's expression.
                                     Test attribute name can be used with no receiver in EXPRESSION.
                                     EXPRESSION examples:
                                       !slow
                                       tag == 'important' and !slow
        --[no-]priority-mode         Runs some tests based on their priority.
        --default-priority=PRIORITY  Uses PRIORITY as default priority
                                     (h[igh], i[mportant], l[ow], m[ust], ne[ver], no[rmal])
    -I, --load-path=DIR[:DIR...]     Appends directory list to $LOAD_PATH.
        --color-scheme=SCHEME        Use SCHEME as color scheme.
                                     (d[efault])
        --config=FILE                Use YAML fomat FILE content as configuration file.
        --order=ORDER                Run tests in a test case in ORDER order.
                                     (a[lphabetic], d[efined], r[andom])
        --max-diff-target-string-size=SIZE
                                     Shows diff if both expected result string size and actual result string size are less than or equal SIZE in bytes.
                                     (1000)
    -v, --verbose=[LEVEL]            Set the output level (default is verbose).
                                     (important-only, n[ormal], p[rogress], s[ilent], v[erbose])
        --[no-]use-color=[auto]      Uses color output
                                     (default is auto)
        --progress-row-max=MAX       Uses MAX as max terminal width for progress mark
                                     (default is auto)
        --no-show-detail-immediately Shows not passed test details immediately.
                                     (default is yes)
        --output-file-descriptor=FD  Outputs to file descriptor FD
        --                           Stop processing options so that the
                                     remaining options will be passed to the
                                     test.
    -h, --help                       Display this help.

Deprecated options:
        --console                    Console runner (use --runner).

行番号を指定して実行する

rspec の時は、以下のようにして行番号を指定して、特定のテストを実行することができました。

$ rspec spec/xxx_spec.rb:12

これができないテスティングフレームワークへの移行などできるはずもないわけですが、test-unit gem でもできます!--location オプションに行番号を指定してテストを実行することができます。

$ ruby test/test_xxx.rb --location 12

やった! ╭( ・ㅂ・)و ̑̑

テスト名を指定して実行する

--name オプションで特定のテスト(メソッド)を指定してテストを実行することができます。

# test/test_xxx.rb
require 'test/unit'
class TestXXX < Test::Unit::TestCase
  def test_foo
    assert_true(true)
  end
  def test_bar
    assert_false(true)
  end
end
$ ruby test/test_xxx.rb --name test_foo

test_foo のテストだけが実行されます。注意点としては、以下のような test 文字列 do; end の記法を使うと --name オプションを使用できなくなるところです。

# test/test_xxx.rb
require 'test/unit'
class TestXXX < Test::Unit::TestCase
  test "test_foo" do
    assert_true(true)
  end
  test "test_bar" do
    assert_false(true)
  end
end

--location 使えればなんとかなると思っているので、個人的にはあまり気にしていません。

タグを指定して実行する

rspec にはタグという機能があって、タグを指定してテストをフィルタリングして実行することができました。

# stack_spec.rb
describe Stack do
  context "when new" do
    it '#empty?', :current => true do
      should be_empty
    end
    it '#items', :type => 'getter' do
      should have(0).items
    end
  end
end
$ rspec stack_spec.rb --tag current
$ rspec stack_spec.rb --tag type:getter

test-unit gem には属性という機能があって、属性をつけてフィルタリングすることができます。

require 'test/unit'
class TestXXX < Test::Unit::TestCase
  attribute :current, true
  test "test_foo" do
    assert_true(true)
  end
  attribute :type, 'getter'
  test "test_bar" do
    assert_false(true)
  end
end
$ ruby test_xxx.rb --attribute current
$ ruby test_xxx.rb --attribute 'type == "getter"'

Ruby の文法で複雑な条件も指定できます。

$ ruby test_xxx.rb --attribute 'current || type == "getter"'

まとめ

rspec ではできたオプションを test-unit gem ではどのように指定するのか解説してみました。 test-unit gem には他にも priority 指定の機能などもあったりするので、是非調べてみてください!

ドキュメントページ にもこの辺書いてあるといいんだどなぁ|ω・`)チラ