たいていのgemにはユニットテストが用意されていて、ソースコードに変更を加えた後のテストを自動化しています。Rubyで使えるテストフレームワークにはさまざまなものがありますが、姉妹連載の「開発現場でちゃんと使えるRails 4入門」にならい、「RSpec」を使うこととします。RSpecの基本的な概念や使い方は、記事「RailsテストフレームワークRSpecのインストールと基本的な使い方、基礎文法」を参照してください。
テストコードは「/spec」ディレクトリ以下に配置します。「/lib/rpn_calculator/stack_calculator.rb」に対するテストコードは、「/spec/rpn_calculator/stack_calculator_spec.rb」として配置します。
require "spec_helper" RSpec.describe RpnCalculator::StackCalculator do describe "#calc" do # simple patterns context "-2 3 +" do subject { RpnCalculator::StackCalculator.new(["-2", "3", "+"]).calc } it { expect(subject).to eq 1.0 } end context "-2 3 -" do subject { RpnCalculator::StackCalculator.new(["-2", "3", "-"]).calc } it { expect(subject).to eq -5.0 } end context "-2 3 *" do subject { RpnCalculator::StackCalculator.new(["-2", "3", "*"]).calc } it { expect(subject).to eq -6.0 } end context "-2 3 /" do subject { RpnCalculator::StackCalculator.new(["-2", "3", "/"]).calc } it { expect(subject).to eq (-2.to_f / 3.to_f) } end # complex pattern context "2.7 1.1 - -5.2 0.9 + *" do subject { RpnCalculator::StackCalculator.new(["2.7", "1.1", "-", "-5.2", "0.9", "+", "*"]).calc } it { expect(subject).to eq -6.88 } end end describe "#numeric?" do context "the argument can be converted to float" do subject { RpnCalculator::StackCalculator.new([]).send(:numeric?, "3.14") } it { expect(subject).to be true } end context "the argument can not be converted to float" do subject { RpnCalculator::StackCalculator.new([]).send(:numeric?, "+") } it { expect(subject).to be false } end end end
6行目から31行目にかけて、calcメソッドのテストが記述されています。基本的な四則演算と、少し複雑な例をテストケースとして用いています。
加えて、33行目から43にかけてnumeric?メソッドのテストが記述されています。numeric?メソッドはprivateになっているので、そのままでは呼び出すことができません。そこで、35行目と40行目では、sendメソッドを用いることでnumeric?メソッドを呼び出し、テストしています。
せっかくなので、RnpCalculatorクラスのクラスメソッド「run」のテストも書いておきましょう。
標準出力の内容をテストしたいので、「/rnp_calculator.gemspec」の「spec.add_development_dependency "rspec"」の下に、以下の1行を加えてください。
spec.add_development_dependency "ariete"
「Ariete」は、筆者が開発しているgemで、RSpecを拡張して標準出力や標準エラー出力をキャプチャしてテストできるようになります。
「/rnp_calculator.gemspec」に上述の1行を書き加えたら、あらためて以下のコマンドで依存ライブラリをインストールしてください。
$ bundle install --path vendor/bundle
次に、RSpecでArieteを有効にするために、「/spec/spec_helper.rb」を編集します。最終行の「require 'ariete'」がポイントです。
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) require 'rpn_calculator' require 'ariete'
RnpCalculatorモジュールのテストコードは以下のようになります。
require "spec_helper" RSpec.describe RpnCalculator do describe ".run" do subject { -> { RpnCalculator.run(["2.7", "1.1", "-", "-5.2", "0.9", "+", "*"]) } } it { expect(subject).to be_output "-6.88\n" } end end
ArieteのRSpec拡張によって、be_outputというRSpecの述語が使えるようになります。be_outputを使うことで、6行目のように、標準出力に出力された内容と期待値を比較し、一致するか異なっているかを調べることができます。
では、RSpecによるテストを実行してみましょう。以下のコマンドを実行してみてください。
$ bundle exec rspec
全てのテストにパスし、以下のような出力が得られたら成功です!
RpnCalculator::StackCalculator #calc -2 3 + should eq 1.0 -2 3 - should eq -5.0 -2 3 * should eq -6.0 -2 3 / should eq -0.6666666666666666 2.7 1.1 - -5.2 0.9 + * should eq -6.88 #numeric? the argument can be converted to float should equal true the argument can not be converted to float should equal false RpnCalculator .run should be output "-6.88\n" Finished in 0.00499 seconds (files took 0.11788 seconds to load) 8 examples, 0 failures
もしテストに失敗した場合は、失敗したテストケースのエラーメッセージを読んで、適宜修正してください。
以上でアプリケーションそのものはひと通り完成です。最後に、作成したアプリケーションをパッケージ化して、.gemファイルを生成してみましょう。以下のコマンドを実行してみてください。
$ bundle exec rake build
ビルドに成功すると、「/pkg/rpn_calculator-0.0.1.gem」というファイルが生成されます。
生成したgemを他の環境にインストールしたい場合は、以下のようにgemコマンドでインストールできます。
$ gem install rpn_calculator-0.0.1.gem
インストールが完了すると、以下のようにして逆ポーランド記法計算機を使えるようになります。
$ rpn_calculator 2 3 + 5.0
ここまでの作業で、それっぽい逆ポーランド記法による計算機を作ることができました。しかしながら、改良すべき点はたくさんあります。
改良点の一つの例として、以下のようなコマンドを実行してみましょう。
$ bundle exec bin/rpn_calculator 5 3 $
すると、以下のような出力が得られるでしょう。
/Users/flost/var/repo/diy/ruby/rpn_calculator/lib/rpn_calculator/stack_calculator.rb:15:in `block in calc': undefined method `$' for 5.0:Float (NoMethodError) from /Users/flost/var/repo/diy/ruby/rpn_calculator/lib/rpn_calculator/stack_calculator.rb:9:in `each' from /Users/flost/var/repo/diy/ruby/rpn_calculator/lib/rpn_calculator/stack_calculator.rb:9:in `calc' from /Users/flost/var/repo/diy/ruby/rpn_calculator/lib/rpn_calculator.rb:8:in `run' from bin/rpn_calculator:5:in `<main>'
エラーメッセージを見てみると、例外NoMethodErrorが出ています。これは、数式の最初の5.0に対して、「$」という存在しないメソッドを実行しようとしたことが原因です。
使えない演算子を使おうとしたユーザーの責任だとも言えますが、使い勝手を上げるためにも、この例外をキャッチして適切なエラーメッセージを出力すべきでしょう。
rpn_calculatorは筆者のGitHubリポジトリに置いてあります。GitHubでのソーシャルコーディングの練習がてら、素敵な改良やバグ修正をしたら、ぜひプルリクエストを送ってください。一緒にrpn_calculatorを育てていきましょう!
連載最終回となる今回は、これまでの総まとめとして簡単な逆ポーランド記法による計算機を作り、gemとしてパッケージ化しました。本連載の知識を総動員すれば、素敵なアプリケーションやライブラリを作って公開できるでしょう!
今回を持ちまして、本連載は終了となります。私の不慣れなつたない草稿に温かい指摘をくださった関係者の皆さま、そしてこれを今まさに読んでくださっている読者の皆さまに感謝いたします。ここまで連載を続けてこられたのも、皆さまの支えがあったからこそです。
本連載を通じて、変幻自在なRubyの面白さと、日々のエンジニアライフに生かすことのできるRubyの技術が少しでも伝われば幸いです。
本連載と「開発現場でちゃんと使えるRails 4入門」連載の監修者の山根です。今回を持ちまして、Ruby連載とRails連載はともには終了になります。今回初めての経験の監修業ということで、執筆者の麻田さん、林さんはじめ、記事を読んでくださった読者の皆さま方には本当に感謝をしています。本連載が、皆さま方のプログラミングライフの質と幸福度を向上する一助となれば、執筆関係者一同、これ以上の幸せはありません。ありがとうございました。
麻田 優真(Rails技術者認定シルバー試験問題作成者)
イタリア、ローマ生まれ。中学生のころHSPに初めて触れ、プログラミングの楽しさを知る。オープンソースやハッカーカルチャーを好む。C#からRubyに転向したときに、動的型付け言語の柔軟性やメタプログラミングの魅力に感動し、Rubyとともにプログラマーとしての人生を歩む決意を固める。
現在は奈良先端科学技術大学院大学で学生として所属するかたわら、株式会社アジャイルウェアでプログラマーとして従事。Ruby on Railsによるコンシューマー向けのWebサービスの開発などに尽力している。
好きなメソッドは、define_method。
Twitter:@Mozamimy、ブログ:http://blog.quellencode.org/
山根 剛司(Ruby業務開発歴7年)
兵庫県生まれ。1997年からベンチャー系のパッケージベンダーで10年間勤務。当時、使用していた言語はJavaとサーバーサイドJavaScript。
2007年よりITコンサル会社に転職し、Rubyと出会って衝撃を受ける。基幹システムをRuby on Railsで置き換えるプロジェクトに従事。それ以来Ruby一筋で、Ruby on Railsのプラグインやgemも開発。
2013年より、株式会社アジャイルウェアに所属。アジャイルな手法で、Ruby on Railsを使って企業向けシステムを構築する業務に従事。
Ruby関西所属。
Twitter:@spring_kuma、Facebook:山根 剛司
Copyright © ITmedia, Inc. All Rights Reserved.