I am a Sr. Software Developer at Oracle Cloud. The opinions expressed here are my own and not necessarily those of my employer.
Redis and testing your code
In previous posts I blogged about using Redis to store data. The question is how to test the code that leverages this data?
When your Rails tests use records from the main DB (MySQL, PG, Mongo) you can use gems like FactoryGirl and DatabaseCleaner to get data into the right state for your tests. How do you do the same with Redis?
I am using mock_redis gem which helps avoid having Redis instance up just to run tests (makes it easier to run tests via Travis CI or CodeShip).
Configure Redis connection in config/initializers/redis.rb
unless Rails.env.test?
REDIS = Redis::Namespace.new(:my_namespace,
redis: Redis.new(host: Rails.application.config.redis_host, port: 6379, db: 0) )
else
REDIS = Redis::Namespace.new(:my_namespace, redis: MockRedis.new )
end
In spec/rails_helper.rb
# add this at the top
require 'mock_redis'
...
config.before(:each) do
# data is not saved into real Redis but you still need to clear it
REDIS.flushdb
end
...
Here we have app/services/redis_service_object.rb that returns values of all keys in Redis.
class RedisServiceObject
def perform
output = []
REDIS.keys.each do |key|
# assumes you are only storing strings
output << REDIS.get key
end
return output
end
end
In spec/services/redis_service_object_spec.rb
require 'rails_helper'
describe RedisServiceObject, type: :service do
before(:each) do
# setup your data here
REDIS.set 'key1', 'value1'
REDIS.set 'key2', 'value2'
end
it 'valid test' do
# use contain_exactly instead of eq because keys can be returned in different order
expect(RedisServiceObject.perform).to contain_exactly('value1', 'value2')
end
end
This solution works well when you have a few objects accessing data in Redis and you can just create the data in your tests. This approach is not very DRY if you have lots of code that needs to be tested. In that case you could create separate class(es) somewhere in spec/factories and call them from your test like any other Ruby class.
With mock_redis test data is never actually persisted into Redis. If that is not sufficient then instead of configuring MockReids in initializer you can use the real Redis connection (and have Redis server running). You could separate environment specific data by using different Redis DBs and/or namespaces.
There is also fakeredis gem but I could not make it work with latest Rspec, Rails and Redis. If someone knows how to use it, please leave a comment.