Redis is a bucket, a dump or just a key value store. It is quite convenient for caching.
When testing business logic on top of it, some issues should be considered.
Redis business logic
A simple Redis wrapper is used as an examplary implementation for some business logic based on Redis.
The wrappers goal is to persist objects into Redis, but with a unique key.
One use case could be different kind of ActiveRecord objects with the same ID. They can not be dumped into Redis in a unique way per se. That wrapper builds a distinct key consisting of the objects class name and ID:
module Model
class Cache
attr_reader :record
def initialize(record)
@record = record
end
def dump(value)
redis.set key, value
end
def find
redis.get key
end
private
def key
"#{record.class}:#{record.id}"
end
def redis
@redis ||= Redis.new
end
end
end
Based on the distinct key, the wrapper can persist and find a value.
The same approach is used by similar existing Ruby Gems.
This implementation has to be tested with RSpec.
Redis spec helper
At first it makes sense to add a Redis spec helper, that cares about repeating tasks, which are not necessary for the specification readability. A minimum redis_spec_helper.rb:
RSpec.configure do |config|
config.before(:each) { redis.flushdb }
config.after(:each) { redis.quit }
end
It flushes all Redis storages before each test and quits the connection to Redis afterwards.
It also could include setting a dedicated test Redis database instance or other test helpers.
Unit test preparations for Redis
The unit test demands a few test preparations. In particular the testable Model::Cache object as the subject:
require 'rspec'
require 'redis_helper'
TestModel = Struct.new :id
RSpec.describe Model::Cache do
let!(:redis) { Redis.new }
let!(:model) { TestModel.new rand(1..10) }
let!(:model_key) { "TestModel:#{model.id}" }
subject { Model::Cache.new model }
describe '#model' do
it 'returns the model' do
expect(subject.model).to be model
end
end
end
Furthermore several variables are created, namely:
- redis (an independent Redis database access)
- model (a Struct object with a random ID, playing the model)
- model_key (the expected model key)
After all the model accessor model is tested already.
Unit tests with Redis
At least the tests for both methods dump and find cover the expected behaviour:
require 'rspec'
require 'redis_helper'
TestModel = Struct.new :id
RSpec.describe Model::Cache do
let!(:redis) { Redis.new }
let!(:model) { TestModel.new rand(1..10) }
let!(:model_key) { "TestModel:#{model.id}" }
subject { Model::Cache.new model }
describe '#model' do
it 'returns the model' do
expect(subject.model).to be model
end
end
describe '#dump' do
before { subject.dump 'test' }
it 'dumps the models key into Redis' do
expect(redis.keys).to eq([model_key])
end
it 'dumps the value for the models key into Redis' do
expect(redis.get(model_key)).to eq('test')
end
end
describe '#find' do
it 'finds the records value' do
redis.set model_key, 'test'
expect(subject.find).to eq('test')
end
context 'when dumps an Integer' do
it 'returns the value as a String' do
redis.set model_key, 123
expect(subject.find).to eq('123')
end
end
end
end