須藤です。

test-unit 3.0.0をリリースしました。

Web: http://test-unit.github.io/ja/#test-unit
インストール: gem install test-unit

test-unitとは:

test-unitは

  テストコードもいつも通りのRubyのコードとして書けること

を大事にしているxUnit系の単体テストフレームワークです。

私たちはRubyで楽しくプログラムを書いているはずなので、テスト
を楽しく書くためにはいつも通りコードを書けることが大事だとい
う考えです。

このリリースについて:

2.0.0をリリースしたのが2008-06-18なので、6年ぶりのメジャーバー
ジョンアップリリースです。

今回の目玉は辻本さんによるPower Assertのサポートです。

参考: http://www.callcc.net/diary/20140531.html#p01

以下、少し長めの説明が続きます。。。

テスティングフレームワークのassertionがすることは次の2つです。

  * 実行結果のチェック
  * 期待した結果ではなかったときのレポート

従来のassertionとPower Assertはレポート時に注目するものが違
います。従来のassertionは実行結果に注目し、Power Assertは実
行過程に注目します。

  * 従来のassertion: 実行結果に注目
  * Power Assert:    実行過程に注目

例を見てみましょう。
test_normal_assertionが従来のassertionで、
test_power_assertがPower Assertです。
どちらも失敗するテストです。

--
require "test-unit"

class AssertionTest < Test::Unit::TestCase
  def test_normal_assertion
    assert_equal(String, "3".to_i.times.map {|i| i + 1 }.class)
  end

  def test_power_assert
    assert do
      "0".class == "3".to_i.times.map {|i| i + 1 }.class
    end
  end
end
--

これを実行するとこうなります。

まず、従来のassertionの方です。

--
test_normal_assertion(AssertionTest)
/tmp/a.rb:5:in `test_normal_assertion'
     2: 
     3: class AssertionTest < Test::Unit::TestCase
     4:   def test_normal_assertion
  => 5:     assert_equal(String, "3".to_i.times.map {|i| i + 1}.class)
     6:   end
     7: 
     8:   def test_power_assert
<String> expected but was
<Array>

diff:
? String
? A  ray
--

実行結果に注目しているので、結果が期待値とどう違うかをレポー
トすることを頑張ります。diffを出しているのはそのためです。

次はPower Assertの結果です。

--
        "0".class == "3".to_i.times.map {|i| i + 1}.class
            |            |    |     |               |
            |            |    |     |               Array
            |            |    |     [1, 2, 3]
            |            |    #<Enumerator: 3:times>
            |            3
            String
test_power_assert(AssertionTest)
/var/lib/gems/2.1.0/gems/power_assert-0.1.3/lib/power_assert.rb:223:in `start'
/tmp/a.rb:9:in `test_power_assert'
      6:   end
      7: 
      8:   def test_power_assert
  =>  9:     assert do
     10:       "0".class == "3".to_i.times.map {|i| i + 1}.class
     11:     end
     12:   end
--

実行過程に注目しているので、途中の式の結果を表示しています。

(Power Assertの実装の中にはdiffも出すものもあります。)


私、Power Assertはキライだったんですが、考えを改めました。
test-unitは、述語メソッド(真偽値を返すメソッド)のテストに
はPower Assertを推奨することにします。
(値が等しいかどうかチェックにはこれまで通りassert_equalを推
奨します。)

述語メソッドのテストとは次のようなテストです。

  assert_true(File.exist?("README"))
  assert_include([1, 2, 3], 2)
  assert_predicate([], :empty?)

test-unitは「いつも通りのRubyのコードで書けること」を大事に
しているので、File.exist?(...)のテストをする場合はテストでも
File.exist?(...)と書けるようにしたいわけです。しかし、

  assert_true(File.exist?("README"))

と書くと、失敗したときは次のような情報しか出せません。

  <true> expected but was
  <false>

このテストでは「READMEがなかった」という情報が大事なのにそれ
を出せていません。これは、assert_trueが「exist?」というメソッ
ドを使ったという情報もexist?の引数の情報を持っていないからです。

これを改善するために

  assert_include([1, 2, 3], 2)
  assert_predicate([], :empty?)

というassertionが存在します。これらはどんな操作をしたいかと
いう情報も引数の情報も持っています。そのため、「READMEがなかっ
た」のような情報を出せます。しかし、「いつも通りのRubyのコー
ド」ではありません。

そこでPower Assertです。

   path = "README"
   assert do
     File.exist?(path)
   end

が失敗すると次のようにレポートします。

        File.exist?(path)
        |    |      |
        |    |      "README"
        |    false
        File

操作も引数もわかり、いつも通りのRubyのコードを書けています。

そのため、test-unitでは真偽値を返すメソッドのテストにはPower
Assertを推奨します。


(↑みたいな内容をるびまに寄稿しようと思っているんですけど、
9月リリースには間に合わせられる気がしないんですよね。。。)


お知らせ:

RubyForgeの終了に伴い、test-unitのコミットメールは

  https://groups.google.com/forum/#!forum/test-unit-commit

に流すことにしました。test-unitの開発に興味のある人は購読し
てみてください。