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

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

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

AP4R におけるテストサポート

AP4Rでのテストの方法には,functionalとasyncの2種類があることに触れました。ここからは,それぞれの動作概要を確認した後,テストの書き方と実行について説明します。

テストの動作概要

1つ目は,Rails付属の3種類の方法のひとつであるfunctionalテストを用います。アクション単体のテストを行う際に,メッセージのキューイングをスタブ化して,実際には通信無しでテストを行います(業務DBへの通信は残っています⁠⁠。これは,ネットワーク通信など,外部プロセスに関連するテストを行う際に良く用いられる手法です。

図5 functionalテストではキューイングをスタブ化して通信無しでテストを実行

図5 functionalテストではキューイングをスタブ化して通信無しでテストを実行

このテスト方法の利点は,メモリ上でテストの実行が完結するため,テストを速く実行出来ることです。つまり繰り返し実行しやすいテストになっています。一方,実際の動作とは異なるため,ある種のエラー処理のテストが難しいことや,場合によっては,オブジェクト直列化(serialization)の制限など,通信ライブラリに特有の問題発見が出来ないこともあり,他のテスト方法にて補完する必要があります。

2つ目は,⁠asyncテスト」という方法で実行するものです。この方法では,実際の通信を含め,実動作と(なるべく)近いテストを行います。ネットワーク通信や,メッセージDBへのメッセージ保存なども行います。この方法が,先程のfunctionalテストの弱点を補完するテストになります。

図6 asyncテストではプロセス間の通信を含めてテストを実行

図6 asyncテストではプロセス間の通信を含めてテストを実行

この2種類のテストは,それぞれActionMailerのメール送信のスタブ化,およびRailsプロセスを起動して行うSeleniumでのテストと比較してもらうと理解しやすいかもしれません。

functionalテストの書き方と実行

functionalテストでAP4R特有なところは以下の2点です。

  1. キューイングをスタブ化するコードを読み込む
  2. キューイングされたメッセージに対してアサーションを書く

まず,スタブ化のコード読み込みのためにas_rails/test/test_helper.rbに以下の行を付け加えます。

require 'ap4r/queue_put_stub'

テストコードは,controllerを生成した際に作成されているas_rails/test/functional/async_shop_controller_test.rbに記述します。

ここでは,order アクションを呼び出して,メッセージ1件のキューイングを確認するテストを書くことにしましょう。

  def test_order
    post :order, :order => {:item => "introduction to AP4R"}
    assert_response :redirect
    assert_redirected_to :action => 'index'

    messages = @controller.ap4r.queued_messages                                       # ... (1)
    assert_equal 1, messages.keys.size, "should have messages in just ONE queue"
    assert messages.key?("queue.async_shop.payment"), "queue name is INCORRECT"       # ... (2)

    assert_equal 1, messages["queue.async_shop.payment"].size,
      "should have just ONE message for payment"
    assert_match /order_id=\d+/, messages["queue.async_shop.payment"].first[:body],
      "parameter order_id should be included with a numeric value"                    # ... (3)
  end

(1)では,AP4Rのクライアント@controller.ap4rを通じて,非同期処理のメッセージにアクセスしています。通信のスタブ化により,このクライアントはキューイングされるべきメッセージをメモリ上に保持するようになっているのです。このメッセージが詰った Hash オブジェクトは,キュー名をキーとして,メッセージ本体:bodyとヘッダー:headersの組を配列として持つ構造となっています。具体的に,(1)で得られるオブジェクトをYAML形式で表すと次のようになっています。

---
queue.async_shop.payment:
  - :body: order_id=1
    :headers:
      :target_method: POST
      :delivery: :once
      :dispatch_mode: :HTTP
      :queue_name: queue.async_shop.payment
      :target_url: http://test.host/async_shop/payment

このオブジェクトに対し,正しい名前のキューに入っているか(2),メッセージ本体に order_id というキーを含むかどうか(3)を確認しています。

functionalテストの実行は,rakeを通じて,または直接テストクラスを読み込むことで行います。as_railsディレクトリにて,以下のコマンドを実行します。

% rake test:functionals
または
% ruby test/functional/async_shop_controller_test.rb

テストが成功した場合,以下のような表示になります。

Finished in 0.066875 seconds.

2 tests, 7 assertions, 0 failures, 0 errors

このようにキューイングをスタブ化することで,単一アクションのテストを行うことが出来ました。

著者プロフィール

加藤究(かとうきわむ)

フューチャーアーキテクト株式会社,シニアコンサルタント。土木専攻だった大学時代には,道路や橋の建設について学んできたが,社会に出てからは 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/