Araoの技術ブログ

見習いエンジニアのAraoが、学んだことなどを書いたりするブログです。

Rubyの研修その1 - 自動テストに挑戦してみた

Ruby勉強中の僕が、簡単な自動テストに挑戦してみた話です。
この記事で使っているRubyのバージョンは2.1.4です。

まず、研修の課題でこんなものを書きました。コンピュータとじゃんけんで遊ぶことができる、夢のようなプログラムです。

janken.rb

janken = ["g", "c", "p"]
output = {"g" => "グー", "c" => "チョキ", "p" => "パー"}

you = gets.chomp
if !janken.include?(you) then
    puts "g, c, p のいずれかを入力してください。"
    exit(1)
end
com = janken.sample

print "あなたは", output[you], "、私は", output[com]
if you == "g" && com == "c" || you == "c" && com == "p" || you == "p" && com == "g" then
    print "、あなたの勝ちです。\n"
elsif you == com then
    print "、引き分けです。\n"
else
    print "、あなたの負けです。\n"
end

実行すると、標準入力の待ち状態になって、ユーザはg, c, pのいずれかを入力します。コンピュータはランダムにグー、チョキ、パーを出し、勝ち負けを判定し、結果を出力します。
ためしにやってみるとこんな感じになります。

$ ruby janken.rb
g
あなたはグー、私はパー、あなたの負けです。

さて、ここまで書いたところで、次の課題が言い渡されました。

偉い人「じゃんけんクラスを作って、勝ち負け判定だけを自動テストしてみましょう。自動テストはminitestを使いましょう(外のテストフレームワークでもいいけど)」

クラスはまあいいとして、minitestのことは全く知らなかったため、とりあえず調べてみることにします。頼りになるぜGoogle先生

参考:library minitest/unit

ふむふむ。わかったようなわからないような。メソッドの戻り値が期待通りになっているかどうか確かめる、という認識で良いのかしら。このライブラリは2.2.0で削除されましたって書いてあるけど、僕が今使っているのは2.1.4だからセーフ?

今回のケースではどんな風に書けばいいのか、偉い人に聞いてヒントをもらったりしながら、こんなものを作りました。
まずはテストの対象となるじゃんけんクラス。

class_janken.rb

class Janken
    def dasu(you, com)
        @you = you
        @com = com
    end
    def judge
        if @you == "g" && @com == "c" || @you == "c" && @com == "p" || @you == "p" && @com == "g" then
            return "win"
        elsif @you == @com then
            return "draw"
        else
            return "lose"
        end
    end
end

次に、このクラスファイルを呼び出して利用する、メインの部分。これを実行すると、最初に挙げたじゃんけんプログラムと同じようにじゃんけんができます。

main_janken.rb

require './class_janken'

pon = ["g", "c", "p"]
output1 = {"g" => "グー", "c" => "チョキ", "p" => "パー"}
output2 = {"win" => "、あなたの勝ちです。\n", "draw" => "、引き分けです。\n", "lose" => "、あなたの負けです。\n"}

you = gets.chomp
if !pon.include?(you) then
    puts "g, c, p のいずれかを入力してください。"
    exit(1)
end
com = pon.sample

janken = Janken.new
janken.dasu(you, com)
print "あなたは", output1[you], "、私は", output1[com], output2[janken.judge]

最後に、今回のキモであるテストファイル。じゃんけんクラスから作成したオブジェクトに、グーチョキパーを合計9通り与えて、戻り値が期待通りになるかどうか調べるものです。

test_janken.rb

require 'minitest/autorun'
require './class_janken'

MiniTest::Unit.autorun

class Testjanken < MiniTest::Unit::TestCase
    def setup
        @janken = Janken.new
    end
    def test_gwin
        @janken.dasu("g", "c")
        assert_equal "win", @janken.judge
    end
    def test_gdraw
        @janken.dasu("g", "g")
        assert_equal "draw", @janken.judge
    end
    def test_glose
        @janken.dasu("g", "p")
        assert_equal "lose", @janken.judge
    end
    def test_cwin
        @janken.dasu("c", "p")
        assert_equal "win", @janken.judge
    end
    def test_cdraw
        @janken.dasu("c", "c")
        assert_equal "draw", @janken.judge
    end
    def test_close
        @janken.dasu("c", "g")
        assert_equal "lose", @janken.judge
    end
    def test_pwin
        @janken.dasu("p", "g")
        assert_equal "win", @janken.judge
    end
    def test_pdraw
        @janken.dasu("p", "p")
        assert_equal "draw", @janken.judge
    end
    def test_plose
        @janken.dasu("p", "c")
        assert_equal "lose", @janken.judge
    end
end

さっそくテストを実行してみます。

$ ruby test_janken.rb
(略)
Finished in 0.001686s, 5336.9830 runs/s, 5336.9830 assertions/s.

9 runs, 9 assertions, 0 failures, 0 errors, 0 skips

9通り全てのパターンで、戻り値が期待通りだったようです。めでたしめでたし。

手動でテストを行うと、コンピュータが何を出すかは完全にランダムなので、運が悪いと延々とじゃんけんを続けることになってしまいます。それに対して、自動テストを使うことで、0.002秒足らずで全てのパターンのテストを終えることができました。
今回テストの対象となったプログラムは非常に小さいものだったので、手動テストにかかる時間と自動テストのためのテストファイルの作成にかかる時間を比べると同じぐらいになるかもしれませんが、巨大なプログラムのテストをする際は、作業時間を短縮できるだけでなく、つまらないミスもなくせるので、非常に役に立ちそうですね。