AP4R,Rubyで非同期メッセージング

第3回 SAF機能とテストサポートによる安心非同期

この記事を読むのに必要な時間:およそ 10 分

asyncテストの書き方と実行

非同期処理のテストで実際に通信を行うには,複数のプロセスを起動する必要があります。AP4Rプロセスに加え,AP4RからHTTPで呼び出されるRailsのプロセスも起動しておく必要があります。

まとめると,asyncテストでは,以下の3つのプロセスが登場します。

  • テストのクライアントなるプロセス: Test::Unit を実行
  • Railsプロセス: mongrel_railsを実行
  • AP4Rプロセス: mongrel_ap4rを実行

これらのプロセス間の関係は,前ページ図6になります。

テストの実行方法の前に,テストコードを見てみます。as_rails/test/asyncディレクトリを作成し,ap4r_test_helper.rb と async_shop_test.rbを以下の内容で作成します。

ap4r_test_helper.rb

ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
require "ap4r/service_handler"

ap4r_test_helper = Ap4r::ServiceHandler.new

require 'test_help'

class Test::Unit::TestCase
  self.use_transactional_fixtures = false
  self.use_instantiated_fixtures  = false

  # Add more helper methods to be used by all tests here...
  cattr_accessor :ap4r_helper

  def ap4r_helper
    @@ap4r_helper
  end

  def with_services(&block)
    ap4r_helper.with_services(&block)
  end
end

Test::Unit::TestCase.ap4r_helper = ap4r_test_helper

async_shop_test.rb

require "#{File.dirname(__FILE__)}/ap4r_test_helper"

require 'net/http'

# Test cases with ap4r integration.
# some comments:
# - Clearance of data in a database and queues should be included.
# - Workspaces of ap4r and rails should have some conventions for convinience.
# - Think about transition to RSpec.
# - HTTP session holding support is needed?
class AsyncShopTest < Test::Unit::TestCase

  def test_http_dispatch
    ap4r_helper.stop_dispatchers     # ... (1)

    assert_rows_added(Order, 1) {    # ... (3)
      do_order                       # ... (2)
    }

    assert_rows_added(Payment, 1) {  # ... (6)
      ap4r_helper.start_dispatchers  # ... (4)
      ap4r_helper.wait_all_done      # ... (5)
    }

  end

  private

  # Requests to <tt>async_shop/order</tt>.
  def do_order(item_name = "test item")
    Net::HTTP.start("localhost", 3000, nil, nil) do |http|
      http.request_post("/async_shop/order",
                        "order[item]=#{item_name}") do |res|
        #nop
      end
    end
  end

  def assert_rows_added(model, rows)
    rows_before = model.count
    yield
    rows_after = model.count
    assert_equal rows, rows_after - rows_before, "table '#{model.table_name}' should count up by #{rows}"
  end
end

テストメソッド test_http_dispatch の中で行っていることは以下のようになります。番号は,コメントにある数字と対応しています。

  1. AP4Rサービスを操作する ap4r_helper を用いて,非同期処理の呼び出しスレッドを停止させます。
  2. Railsへ,HTTP POSTのリクエストを発行し,
  3. orders テーブルに 1 行追加されたことを確認します。
  4. 非同期処理の呼び出しスレッドを開始させ,
  5. 非同期処理が終わるまで待機し,
  6. payments テーブルに 1 行追加されたことを確認します。

それでは,実行してみましょう。

Railsプロセス,AP4Rプロセスの起動

まずは,Railsプロセスと AP4Rプロセスを起動します。プロセス起動のまえに,これまで開発で用いていたRailsやAP4Rのプロセスが起動していないことを確認しておいてください。

Unix系のOSでは,AP4Rプラグインが提供するRakeのタスクで起動できます。設定ファイルを作成した後,Rakeコマンドを実行してください。

設定ファイルは下記のとおりです。

as_rails/config/test_async.yml

ap4r:
  root_dir: ../as_ap4r
  config_file: config/queues_mysql.cfg
rails:
% cd as_rails
% rake test:asyncs:arrange

Windowsでは,このRakeコマンドは使えませんので,以下の2つのコマンドを,別々のコマンドプロンプトで実行してください。

1つ目のコマンドプロンプト
% cd as_rails
% ruby script/server -e test
2つ目のコマンドプロンプト
% cd as_ap4r
% ruby script/mongrel_ap4r start -A config/test_queues.cfg
テストの実行

テストの実行は,rakeを通じて,またはテストクラスを直接読み込んで行います。

% rake test:asyncs:run
または
% ruby test/async/async_shop_test.rb

テストが成功すると,以下の表示となります。

Finished in 5.530491 seconds.

1 tests, 2 assertions, 0 failures, 0 errors

今回は,非同期処理である payment アクションも実行されているため,⁠スリープ時間である)5秒程度の時間がかかっていることが分かります。

後片付け

起動しているプロセスを終了させます。Unix系のOSではRakeタスクで終了させることが出来ます。

% cd as_rails
% rake test:asyncs:cleanup

Windowsでは,RailsとAP4Rを実行している2つのコマンドプロンプトで Ctrl-C を押すことで,それぞれのプロセスを終了させてください。

アクション単体のテストを手軽に行うためのfunctionalテストと,通信を含めて実動作に近いテストを実行するasyncテストの2つの方法を説明しました。このテストサポートでは,Rubyの柔軟さを活用させてもらっています。functionalテストでのスタブ化は,オープンクラスの性質を利用しています。またAP4R自体は(reliable-msgを通して)druby上に実装されているため,色々なところでdrubyの恩恵を得ています。asyncテストでは,AP4Rプロセスの制御のためにネットワーク越しにdrubyで通信を行いながらテストが実行されています。

テストサポートは追加されたばかりの機能で,まだ骨組みだけの状態です。テストコードの可読性を向上させるためのボキャブラリ(API)整備や,テスト自動化,異常系のテスト容易性などは今後向上させていく予定です。

第3回では,安心のためのSAF機能とテストサポートをみてきました。SAFによりat-least-onceという実行時の堅牢さが保証され,メッセージが消失しない安心を得ることができます。そして,非同期まで含めたアプリケーションの品質を保証するテストの仕組みにより,TDD/BDDと同じように開発,リリースに対する安心感を得ることができるのではないでしょうか。

最終回となる次回は,負荷分散のための設定と,SAF Forwardエラーのリカバリ,及び業務処理がエラーとなったメッセージのリカバリを説明する予定です。

著者プロフィール

加藤究(かとうきわむ)

フューチャーアーキテクト株式会社,シニアコンサルタント。土木専攻だった大学時代には,道路や橋の建設について学んできたが,社会に出てからは Javaのメッセージングミドルウェアの開発/保守などに従事。昨年,Rubyで書いたメッセージングライブラリ,AP4Rでオーンソースの世界に仲間入り。現在は,AP4Rの開発/導入サポート中。

URLhttp://d.hatena.ne.jp/kiwamu/


篠原俊一(しのはらしゅんいち)

フューチャーアーキテクト株式会社,シニアコンサルタント。大学時代は物理学を専攻,素粒子論を研究,10次元の世界に住んでいた。入社後まもなく,Martin FowlerのblikiでRubyを知り,"The Ruby Way"に学ぶ。 Rubyとオープンソースが大好きなプログラマとして,AP4Rを開発中。

URLhttp://d.hatena.ne.jp/ita-wasa/