hello-world
webエンジニアのメモ。とりあえずやってみる。

[rails][rspec]database-cleanerを使ってredisのテストデータを削除する

公開日時

今更ながら最近ちゃんとredisを使い始めました。

これまでは使ったとしてもキャッシュやsession_storeにしか使っていなかったのですが、setやsorted setを使うと色々できて奥が深いですね。

railsからredisのデータを扱う際には、 redis-objectsを使うと便利でした。

redis-objectsの設定方法に関しては、Gemfileにredis-objectsを追加し、(namespaceを設定したかったのでredis-namespaceも使っています)

# Gemfile

gem 'redis-objects'
gem 'redis-namespace'

rails_configの設定にredisの接続情報を記述します。

# config/settings.yml

redis:
  host: localhost
  port: 6379
  object_db: 1

redisの接続情報をinitializerに記述。

# config/initializers/redis.rb

namespace = [Rails.application.class.parent_name, Rails.env, 'object'].join(':').downcase
Redis.current = Redis::Namespace.new(namespace, :redis => Redis.new(:host => Settings.redis.host, :port => Settings.redis.port, :db => Settings.redis.object_db))

namespaceを指定しつつ、dbも分けて管理することにしています。(デフォルトのdbは0番)

あとはmodelでRedis::Objectsをincludeすると、redis-objectsが使用可能になります。

詳しい使い方に関しては redis-objectsのgithubに記載されているので参照ください。

今回つまづいたのはredis-objectsを使ったモデルのテストをRSpecで書こうとした際に、テストデータをどうやって削除するかということでした。

ActiveRecordの場合は database_cleanerを使ってテスト実行の度にデータをリセットするようにしていたので、redsでも同様のことができないか調べたところ、database_cleanerはredisもサポートしてくれていました。

ただ、after(:each)でDatabaseCleaner.cleanを実行するだけだとredsのデータが残ったままになってしまったので、 DatabaseCleaner[:redis].clean を実行するようにしました。

また、デフォルトだとlocalhost:6379のdb0に対してflushdbが実行されるので、ローカル開発や他のプロジェクトでdb0を使用しているとテストを実行すると他のデータも消されてしまいます。

これを防ぐためにはstrategy設定の際に、redisのkeysコマンドに指定する値を設定することで特定のキーのみ削除ということができるようです。

# spec/support/database_cleaner.rb

DatabaseCleaner.strategy = :truncation, {:only => %w[*test* ]}  # *test*に該当するキーのみ削除

もしくはテスト用にdbを分けてしまうのが手っ取り早くできそうです。

今回はテスト用にdbを分ける方針で進めることにしました。

例としてdb2番をテストデータ専用dbとします。

最終的なdatabase_cleanerの設定は以下のようになりました。

# spec/support/database_cleaner.rb

RSpec.configure do |config|
  config.before(:suite) do
    DatabaseCleaner[:redis].db = 'redis://localhost:6379/2' # デフォルト設定(0番をテストdbにする)の場合はこの行がなくてもok
    DatabaseCleaner.strategy = :truncation
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
    DatabaseCleaner[:redis].clean # flushdbを実行
  end
end

これでdatabase_cleanerはテスト実行の度にdb2番にflushdbを実行してくれるようになります。

ただし、このままだとredis-objectsはdb1にデータを見に行くので、テスト環境の場合のみdb2を見に行くようにinitializerを書き換えます。

# config/initializers/redis.rb

namespace = [Rails.application.class.parent_name, Rails.env, 'object'].join(':').downcase
Redis.current = Redis::Namespace.new(namespace, :redis => Redis.new(:host => Settings.redis.host, :port => Settings.redis.port, :db => Rails.env.test? ? 2 : Settings.redis.object_db))

これでテスト実行時にredisのデータを削除できるようになりました。

手探りで状態で色々調べながら対応したので、もっと良い方法あれば教えていただけると助かります。

参考


Related #Rails

[rails]ELB使用時にヘルスチェック用のアクションを作成する

ELBにrailsアプリをぶら下げる場合、railsアプリが落ちたらELBから外れてもらいたいです。

[rails]carrierwaveで保存した画像のurlを取得する

carrierwave 0.10.0 で確認 imageカラムにcarrierwaveで保存した画像を保存している場合、デフォルトのままだとだとurlメソッドを実行してもpublic以下のパスのみで http://~ が設定されていません。