注意事項
現在、FakeDynamo はメンテナンスが終了しており、利用は非推奨です。
DynamoDB のユニットテストを実装される方は、DynamoDB Local をご使用ください。
This project is no longer maintained. Checkout Amazon
DynamoDB Local if you are looking for
implementation of latest version.
RSpec を使って DynamoDB Local を用いる方法については、
などが参考になるかと思います。
余談
TravisCI で DynamoDB Local を用いた RSpec によるユニットテストの導入に関わることがあったので、機会があればそちらもまとめてみようと思います。
また、Circle CI への DynamoDB Local の導入も予定しているので、実現したらそちらもまとめようかと思います。
き、気が向いたら!笑
概要
FakeDynamo というローカルで実行できる DynamoDB のエミュレータを利用し、DynamoDB を使用しているプログラムのユニットテストを RSPec で実装してみました。
Amazon DynamoDB
Amazon DynamoDB は、1 桁台のミリ秒単位のレイテンシーを必要とするすべての規模のアプリケーションに対応した高速かつフレキシブルな NoSQL データベースサービスです。
いわゆる NoSQL で、データモデルは Tables, Items, Attributes だそうです。
こちらのスライドにザッと目を通すと良いかと思います。
FakeDynamo
local hosted, inmemory Amazon DynamoDB emulator.
ローカルで動作する Amazon DynamoDB のインメモリなエミュレータです。こいつを DynamoDB の代わりに利用します。
導入
README の Usage 通りに Gem を導入し、起動後に初期化すれば終了です。
なーんとなくポートが使われていないか調べてから起動しました。
$ netstat -na | grep 4567
$ fake_dynamo --port 4567
そして初期化。
$ curl -X DELETE http://localhost:4567
動作確認
DynamoDB へのアクセスに使うモデルはAWS::Record::HashModel
を使って実装しました。
このクラスの使用方法については、[Ruby on Rails] AWS::Record::HashModel を使って Amazon DynamoDB のモデルを作るを参考にしてます。
class User < AWS::Record::HashModel
def self.inherited(subclass)
end
set_shard_name "users"
string_attr :user_id
string_attr :email
end
そして fake_dynamo を起動します。
$ fake_dynamo --port 4567
動作確認ということで、rails console で実行してみました。
$ rails console
# 公式サイトに従い設定
pry(main)> AWS.config(:use_ssl => false,
:dynamo_db_endpoint => 'localhost',
:dynamo_db_port => 4567,
:access_key_id => "xxx",
:secret_access_key => "xxx")
=> <AWS::Core::Configuration>
# テーブルの作成
pry(main)> User.create_table 1, 1, :shard_name => 'users'
AWS::DynamoDB::Errors::ValidationException: Validation error detected: KeySchema must be a Array
from /usr/local/lib/ruby/gems/1.9.1/gems/aws-sdk-1.48.1/lib/aws/core/client.rb:375:in `return_or_raise'
ダメでした。
エラーメッセージで調べてみると Dynamoid という DynamoDB の ORM の Issue にそれらしいコメントがありました。どうやら、古いバージョンを入れたら解決するとのこと。
原因はどうやら、使っている AWS-SDK が古いこと(1系)のようです。
DynamoDB の API Version 2012-08-10 では、2系の AWS-SDK にしか対応してないためした。
You should use an older version of fake_dynamo. You should be able to see the version you are using by typing gem list | grep fake_dynamo in terminal.
To install the right version try gem install fake_dynamo --version 0.1.3.
仕方がないので古いバージョンを入れて再度動作確認!
# 公式サイトに従い設定
pry(main)> AWS.config(:use_ssl => false,
:dynamo_db_endpoint => 'localhost',
:dynamo_db_port => 4567,
:access_key_id => "xxx",
:secret_access_key => "xxx")
=> <AWS::Core::Configuration>
# テーブルの作成
pry(main)> User.create_table 1, 1, :shard_name => 'users'
=> <AWS::DynamoDB::Table table_name:pixta_credentials>
# インスタンスの生成
pry(main)> user = User.new(user_id: 1, email: "foobar@sample.com")
=> #<User:0x000000086e6e70
@_data={"user_id"=>1, "email"=>"foobar@sample.com"},
@_ignore_changes=false,
@_orig_values={"user_id"=>nil, "email"=>nil},
@_shard="users">
# データベースへ保存
pry(main)> user.save
=> true
動きました!
RSpec で FakeDynamo を使う
Google 大先生に聞いたところ、Starting up fake_dynamo automatically with rspec test suiteというステキなサイトを教えていただきました。
こちらを参考にspec/support
内に以下のdynamo.rb
を追加しました。
# FakeDynamoを使うための初期設定
AWS.config(:use_ssl => false,
:dynamo_db_endpoint => 'localhost',
:dynamo_db_port => 4567,
:access_key_id => "xxx",
:secret_access_key => "xxx")
# FakeDynamoをRSpec実行時に実行
RSpec.configure do |config|
dynamo_thread = nil
config.before(:suite) do
FakeDynamo::Storage.db_path = "#{Rails.root}/tmp/db.fdb"
#FakeDynamo::Logger.setup(:warn) # version 0.1.3 には存在しない
FakeDynamo::Storage.instance.load_aof
dynamo_thread = Thread.new do
$stdout = open("/dev/null", "w") # FakeDynamo の標準出力を無効化
FakeDynamo::Server.run!(port: 4567, bind: 'localhost') do |server|
if server.respond_to?('config') && server.config.respond_to?('[]=')
server.config[:AccessLog] = []
end
end
end
end
# FakeDynamoをRSpec終了時に終了
config.after(:suite) do
FakeDynamo::Storage.instance.shutdown
dynamo_thread.exit
end
これで、実行時に FakeDynamo が立ち上がるようになります。本当は良くないですが、詳細については理解せずに流用してます。
Spec ファイルではbefore(:all)
を使い、データベースの初期化とテーブルのクリエイトを最初に行うようにしました。
require 'spec_helper'
describe User do
before(:all) do
# DBの初期化
Net::HTTP.start('localhost', 4567).delete('/')
# テーブルの作成
create_opts = {}
create_opts[:hash_key] = { User.hash_key => :string }
AWS::DynamoDB.new.tables.create(User.shard_name, 1, 1, create_opts)
end
before do
@user = User.new(user_id: 1, email "foobar@hoge.com")
end
subject { @user }
it { should respond_to(:user_id) }
it { should respond_to(:email) }
it { should be_valid }
end
これで DynamoDB を用いたプログラムのユニットテストを RSpec で実装できました。