Edited at

elasticmqを使ったsqsとの連携テスト

More than 3 years have passed since last update.


背景

Amazon SQS。自分のサービスにメッセージキューサービスをとても簡単に組み込めて便利。

http://aws.amazon.com/jp/sqs/

しかし、いざそれを使った機能のspecを書こうと思うと、「外部サービスだからstub書かなきゃ」という辛みと、「結合テスト的に実際にAPI叩きたいし」という欲求が出てきて、どうしたらいいのよ :scream: 、という感じになる。


そりゅーしょん

環境用意すれば良いじゃない!

ElasticMQ

https://github.com/adamw/elasticmq

こちら、Amazon SQS-compatible interfacesという素晴らしい特徴を持ったメッセージキューシステムです。

これをローカルに立ち上げれば、そもそも普通の開発の時からローカルでキューシステム使った検証できるし、何よりrspecからキューへの登録、取得などのテストもちゃんとAPIを叩いた上で通すことができる。


注意

以下は、rails環境でrspecを利用することを想定しています。


準備

まずは、ElasticMQを用意。ローカルに普通にダウンロードしても良いけど、CircleCIとかでの利用を見越すとDockerコンテナとして用意しておくのが良さげ。

FROM java:8

# Create working space
RUN mkdir /var/elasticMQ
WORKDIR /var/elasticMQ

# Default port for SQS Local
EXPOSE 9324

# Get the package from Amazon
RUN wget -q https://s3-eu-west-1.amazonaws.com/softwaremill-public/elasticmq-server-0.8.8.jar

# Default command for image
ENTRYPOINT ["/usr/bin/java", "-Djava.library.path=.", "-jar", "elasticmq-server-0.8.8.jar"]


spec_helper

spec_helperのほうに、以下の設定を書く。

IPはboot2dockerでelasticmqを立ち上げたときの値なので、必要に応じて127.0.0.1とかにすると良い。

ここではサンプルなので、キューの名前も決め打ちで一つだけにしているけど、実際には設定ファイルから読みこんだ値を使うなどよしなに。

# rspec内では常に以下のクライアントを使う 

sqs_client = Aws::SQS::Client.new(
region: 'dummy-sqs',
endpoint: "http://192.168.59.103:9324",
access_key_id: 'dummy',
secret_access_key: 'dummy',
)

regionやアクセスキーなどの値は適当な値でも大丈夫そう(今のところ)。aws-sdkのversionが上がったりするとその辺のvalidationが走ってダメになるかも。今回試したのは2.0.42。

# 事前にqueueを作っておく

# 必要に応じて特定のspecでのみキューを作成するなどにしても良い
config.before(:suite) do
# 都度都度Clientをstubするのが面倒なので`new`自体をstubする
allow(::Aws::SQS::Client).to receive(:new).and_return(sqs_client)
sqs_client.create_queue(queue_name: "sample_jobs")
# http://192.168.59.103:9324/queue/sample_jobs
# が作成したqueue_urlとなる
end

# テストが終わったらqueueのメッセージを消す

# purge処理は即時実行される訳ではないので、次のspec実行時にメッセージが残っている可能性あり
config.after(:each) do
sqs_client.purge_queue(queue_url: "http://192.168.59.103:9324/queue/sample_jobs")
end


spec

以上の準備ができたら、以下のようなモデルとspecを書いてみる。

class JobRegister

class << self
def client
Aws::SQS::Client.new(
region: 'ap-northeast-1' # 実際には決め打ちにすんなよ!
)
end

def register message
client.send_message(
queue_url: "http://192.168.59.103:9324/queue/sample_jobs", # この辺も実際にはよしなにね!
message_body: {
message: message,
}.to_json
)
end
end
end
end


spec/models/job_register_spec.rb

require 'spec_helper'

describe JobRegister do
describe '.register' do
it 'ジョブが登録されていること'
described_class.register "hello"
message = described_class.client.receive_message(queue_url: "http://192.168.59.103:9324/queue/sample_jobs").messages.first
expect(JSON.parse(message.body)["message"]).to eq "hello"
end
end
end

というspecが通るようになる。


注意

queue_urlにlocalhostを使うには、

http://dev.classmethod.jp/server-side/ruby-on-rails/ruby_aws-sdk-core_elasticmq_sqs/

に記載のある通り、パッチを当てる必要がある。しかし、aws-sdkの実装を見る限り"."でsplitした結果からregionを取得してるようなので、"127.0.0.1"のように"."さえ入っていればパッチは当てなくても動いた。


まとめ

とりあえずrspecからelasticmqを叩けた。CircleCIにコンテナを持って行けば、CI環境で結合テストも可能。