はじめに
僕が一番使い慣れているテスティングフレームワークはRSpecです。
普段の業務でも大半はRSpecでテストコードを書いています。
しかし、ごくごく簡単なRubyのコードを書く場合は「わざわざRSpecを書くのは大げさかな」と思うことがあります。
簡単なコードであれば、テストコードも簡単なものになるのでassert_equal
が使えれば十分だったりします。
というわけで今回の記事ではgemを使わず、Ruby標準のテスティングフレームワークでテストコードを書く方法をまとめてみます。
やりたいこと
- 「素のRuby」でぱぱっとシンプルなテストを書きたい(
assert_equal
だけで十分なケースを想定) - gemのインストールはしたくない、他の人にもさせたくない
- Ruby 2.0以降ならどのRubyでも動いてほしい
つまり、 「いつでもどこでも動くテストコードを書きたい」 というのが今回の目的です。
いつでもどこでも動くテストコードの雛形
というわけで、いつでもどこでも動くテストコードの雛形を書くとこんな感じです。
# test_sample.rb
require 'test/unit'
class Sample
def self.greeting
'Hello, world!'
end
end
class TestSample < Test::Unit::TestCase
def test_greeting
assert_equal 'Hello, world!', Sample.greeting
end
end
コードの説明
Test::Unit::TestCase
を継承したクラスを用意し、test_xxx
というメソッドを定義するとそのメソッドがテストの実行対象になります。
ここではそれぞれTestSample
クラスとtest_greeting
メソッドがそれに該当します。
assert_equal (期待値), (実際の値)
で実行結果を検証します。(アサーション)
両者が一致すればテストがパスし、一致しない場合はテストが失敗します。
test_xxx
というメソッドはクラス内に複数あっても構いません。
また、1つのテストメソッド内にassert_equal
を複数書くのもOKです。
(とはいえ、原則として1テストメソッドにつき1アサーションとするのが望ましいです)
参考までにテストメソッドやアサーションを複数書く場合のコード例を以下に示します。
class TestSample < Test::Unit::TestCase
def test_greeting
assert_equal 'Hello, world!', Sample.greeting
end
def test_calc
assert_equal 2, (1 + 1)
assert_equal 1, (2 - 1)
assert_equal 6, (2 * 3)
assert_equal 3, (9 / 3)
end
end
実行方法
実行方法はruby (ファイル名)
でOKです。
Ruby 2.2でもRuby2.0でも動作します。(ただし結果の表示が若干異なります)
# Ruby 2.2で動かした場合
$ ruby test_sample.rb
Loaded suite test_sample
Started
.
Finished in 0.000526 seconds.
1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
1901.14 tests/s, 1901.14 assertions/s
# Ruby 2.0で動かした場合
$ ruby test_sample.rb
Run options:
# Running tests:
Finished tests in 0.002585s, 386.8472 tests/s, 386.8472 assertions/s.
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
ruby -v: ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-darwin14.1.0]
「あれ?Ruby標準のテストフレームワークってMinitestじゃなかったっけ?」
上で紹介したテストコードには「minitest」の文字が出てきません。
普段はRSpecをメインで使っているので、あまりRuby標準のテスティングフレームワークは詳しく見てこなかったのですが、「Ruby 1.9.1以降はMinitestが標準になった」という話をなんとなく聞いていました。
が、Ruby 2.2からはどうも話が変わっているようです。
ネットの情報を元にざっくりと経緯をまとめると、このようになります。
- 1.9.0以前 => Test::Unitが標準
- 1.9.1から2.1まで => Minitestが標準
- 2.2以降 => Minitest 5.4.3とTest::Unit 3.0.8が標準
(参考文献:Rubyのテスティングフレームワークの歴史(2014年版))
もうちょっと細かくいうと、こんなふうになっているようです。
- 1.9.0以前
- Test::Unitが標準なので(当然) Test::Unit向けのコードが動く。
- Minitestは含まれないので(当然) Minitestのコードは動かない。
- 1.9.1から2.1まで
- MinitestがTest::Unit互換のラッパークラスを提供しているため、 Test::Unit向けのコードも動く。
- もちろんMinitestのコードも動く。ただし、Minitestがサポートする構文はバージョン4相当。
- 2.2以降
- Minitestがバージョン5になり後方互換性がなくなったため、Test::Unitが復活。 引き続きTest::Unit向けのコードがサポートされる。
- Minitestのコードも引き続き動く。ただし、Minitestのバージョンが上がったため、Minitest 4のテストコードを実行すると警告が出る。
もっと厳密にいうと、こんな提供方法の違いもあります。
- 2.1以前
- テスティングフレームワークはRuby標準ライブラリの一部として提供されていた。
- 2.2以降
- MinitestもTest::Unitも標準ライブラリではなく tarball (gem)としてバンドルされる。
以上の内容を大雑把にまとめるなら、 「Test::Unitのテストコードは互換性の観点から昔も今も将来もサポートされる(と思う)」 ということになります。
余談:Test::Unit?test-unit?
上の説明では全部まとめて「Test::Unit」と呼びましたが、厳密には「Test::Unit」と「test-unit」は微妙に指しているものが異なるそうです。
- Test::Unit => Ruby 1.9.0までのRuby本体に入っていた標準ライブラリ
- test-unit => Ruby本体から分離され、gemとして開発されているTest::Unit
詳しくは「Rubyのテスティングフレームワークの歴史(2014年版)」をご覧ください。
参考:Minitest向けに書くとこうなる
ちなみに、上のテストコードの雛形をMinitest向けに書くとこうなります。
# Minitest 4までのコード
require 'minitest/autorun'
class Sample
def self.greeting
'Hello, world!'
end
end
class TestSample < MiniTest::Unit::TestCase
def test_greeting
assert_equal 'Hello, world!', Sample.greeting
end
end
# Minitest 5のコード
require 'minitest/autorun'
class Sample
def self.greeting
'Hello, world!'
end
end
class TestSample < Minitest::Test
def test_greeting
assert_equal 'Hello, world!', Sample.greeting
end
end
違いは継承元のクラスです。
Minitest 4ではMiniTest::Unit::TestCase
を、Minitest 5ではMinitest::Test
をそれぞれ継承しています。
Minitestの互換性問題
Ruby 2.2でMinitest 4のコードを動かすと、"MiniTest::Unit::TestCase is now Minitest::Test."という警告が出ます。
また、Ruby 2.1以前のRubyでMinitest 5のコードを動かすと、"`const_missing': uninitialized constant MiniTest::Test (NameError)"というエラーが出てテストコードが動きません。
(ただし、Ruby 2.1以前の環境でもgem install minitest
で最新版のMinitestをインストールすれば、Minitest 5のコードが動くようになります。)
その他、Minitest 4とMinitest 5の互換性問題についてはMinitestのChange logを参考にしてください。
(普段使わないので僕もあまり詳しくないです)
- https://github.com/seattlerb/minitest/blob/master/History.txt => "=== 5.0.0 / 2013-05-10"
参考:RSpecで書くとこうなる
ついでに上の雛形をRSpecでも書いてみましょう。
# sample_spec.rb
class Sample
def self.greeting
'Hello, world!'
end
end
describe Sample do
example 'greeting' do
expect(Sample.greeting).to eq 'Hello, world!'
end
end
実行にはgemのインストールが必要です。
gem install rspec
実行コマンドはrspec (ファイル名)
です。
$ rspec sample_spec.rb
.
Finished in 0.00115 seconds (files took 0.10203 seconds to load)
1 example, 0 failures
RSpecの基本については以前僕が書いたこちらの記事を参考にしてみてください。
単純でないテストコードを書きたいときはどうするの?
この記事で想定しているのは「ごく単純なテストコード」です。
一方でテスト対象のコードによっては「単純でないテストコード」が必要になる場合もあり得ます。
そういう場合はどうしたらよいのでしょうか?
僕の答えは「自分が一番使い慣れているテスティングフレームワークや自分が一番気に入っているテスティングフレームワークを使えば良い」です。
Test::Unitにしろ、Minitestにしろ、RSpecにしろ、それぞれ一長一短がありますし、人の好みもいろいろと分かれます。
なので、「みんなxxxを使え」とひとまとめに結論を出すことはできません。
ちなみに僕の場合はRSpecを使います。
- 一番使い慣れているので迷わず使える
- 標準で便利ないろいろな機能が入っている(gemであれこれ機能追加しなくてよい)
- 公式ドキュメントや書籍、ネット上の情報等が豊富
といった理由からRSpecをメインで使い続けています。
まとめ
というわけでこの記事ではRuby標準のテスティングフレームワークを使って、テストコードを書く方法を紹介しました。
「テストコード = 難しそうだから自分には無理」と思っている初心者の方も多いかもしれませんが、まずは一番簡単な assert_equal (期待値), (実際の値)
だけでも書いてみるのがいいかなと思います。
テストコードを使えば「putsを使って毎回目視で確認」なんてするよりも、高速で確実に実行結果を検証することができます。
また、テストコードを書いておけば他の人も「このメソッドを呼ぶと何が起きるのか」を理解しやすくなります。
Rubyの場合、特別なgemをインストールしなくてもテストコードが書けるようになっているので、まだ書いたことがない人は一度トライしてみましょう!