2010/12/20

Test::Unitを考慮したRubyスクリプトの書き方

DTIのVPSサーバからの連絡をjabber.orgに作ったアカウントで受けるために、RubyでJabberクライアントを作成しました。

単一のRubyスクリプトでクライアントを作成しましたが、その時に少し要件を加えてコードのボリュームが膨らんだので、RubyのTest::Unitフレームワークを使ってテストケースを作成しました。

RubyでTest::Unitを使う場合に、クラス/モジュールファイルにテスト用のコードを加える方法はよくみますが、今回は単体コマンドとしてのスクリプト側にモジュールを寄せて、単体テスト用のコードは別ファイルにしています。

今回使ったRubyのバージョンは1.9.2-p0です。

なおRuby 1.9.xからは単体テスト環境として附属するのはminitestモジュールです。

豊富なアサーションメソッドを使うために、2.1.x系列のTest::Unitモジュールの導入がお勧めです。 → Test::Unit フレームワーク公式サイト

ファイル構造

今回はJabberクライアントの本体をとは別に単体テスト用のスクリプトを作成していて、次のように配置しています。

  • sendmsg.rb
  • ut/test_sendmsg.rb
  • lib/xmpp4r
  • lib/test

lib/testはgemsからインストールした後に gems/gems/test-unit-2.1.2/lib/test/ ディレクトリをコピーしています。

Jabberクライアント本体 スクリプト

sendmsg.rb スクリプトファイル

#!/usr/local/bin/ruby
# -*- coding: utf-8 -*-
$:.unshift File.join([File.dirname($0), "lib"])
require 'xmpp4r'

module YaJabber
  ## ステートレスなメソッド群
  def parse_options
  end
end

if $0 == __FILE__
  include YaJabber
  ## 処理本体が続く
end
単体テスト スクリプト

ut/test_sendmsg.rb スクリプトファイル

#!/usr/local/bin/ruby
# -*- coding: utf-8 -*-
$:.unshift File.join([File.dirname($0), "..", "lib"])
require 'test/unit'

class JabberClientTest < Test::Unit::TestCase
  $:.unshift File.join([File.dirname($0), ".."])
  require 'sendmsg'
  include YaJabber

  def test_parse_options
  end
end

抽象化できる部分は外部ライブラリの形でまとめるべきだとは思いますが、複数のファイルを管理するのはいろいろ面倒な場合もあります。

今回はそこまで難しくない単一のスクリプトファイルを相手にしつつ、Test::Unitを使ってみようとしている場面を想定しています。

規模が小さいとスクリプトをいくつかのシナリオの元で直接実行しても確認はできるので、こういう形式が必要なのか微妙なラインだとは思います。

ただ、こうやって単体スクリプトを作りつつ、他のスクリプトでも使えそうな共通部分があれば、自分用のモジュールライブラリを作れるかなぁ、とか思ってたりしてます。

外部ライブラリの読み込みについて

本当はxmpp4rライブラリやtest/unitライブラリはsite_rubyの中に入れているので、 $:変数を設定する必要はありませんでした。

ただ単体テスト用のスクリプトが"ut"サブディレクトリに入っていたので、スクリプト本体からの相対パスで設定している場合は、単体テストのスクリプトを実行した場合には相対パスの基準がut/サブディレクトリになってしまうため、$:変数を適切に設定する必要があることを忘れないために加えています。

1 件のコメント:

職務経歴書の書き方の見本 さんのコメント...

とても魅力的な記事でした。
また遊びにきます。
ありがとうございます。