Writing tests with RSpec for Redis with Rails

16,147

Solution 1

I like to have redis running while the tests are running. Redis, unlike e.g. postgres, is extremely fast and doesn't slow down test run time noticeably.

Just make sure you call REDIS.flush in a before(:each) block, or the corresponding cucumber hook.

You can test data_to_cache independently of redis, but unless you can fully trust the redis driver you're using and the contract it provides, it's safer to actually test cache_data (and the corresponding cache fetch method) live. That also allows you to switch to a different redis driver (or to a different fast KV store) without a wholesale rewrite of your tests.

Solution 2

First of all add the below code in the spec_helper.rb so you'll be sure that the tests will run on any machine even if the redis server is not installed (make sure to add to your gemfile mock_redis under test scopes:

redis_instance = MockRedis.new
Redis.stub(:new).returns(redis_instance)
Redis::Store.stub(:new).returns(redis_instance)

After that I would test:

  1. The data written to REDIS is the expected data
  2. A sequence of cache_data, flush_data, cache_data calls the data_to_cache twice
Share:
16,147
Kevin Bedell
Author by

Kevin Bedell

About me: Boston-based I build engineering teams and software products. Github http://www.kbedell.com/ @kbedell on Twitter Linkedin profile

Updated on July 02, 2022

Comments

  • Kevin Bedell
    Kevin Bedell almost 2 years

    I have a model class that caches data in redis. The first time I call a method on the model, it computes a JSON/Hash value and stores it in Redis. Under certain circumstances I 'flush' that data and it gets recomputed on the next call.

    Here's the code snippet similar to the one I use to store the data in Redis:

    def cache_data
      self.data_values = data_to_cache
      REDIS.set(redis_key,ActiveSupport::JSON.encode(self.data_values))
      REDIS.get(redis_key) 
    end
    
    def data_to_cache
      # generate a hash of values to return
    end
    

    How should I unit test this code? I use RSpec and Capybara. I also use Cucumber and Capabara for integration testing if that helps.

  • Kevin Bedell
    Kevin Bedell almost 12 years
    So you'd recommend having redis running and actually testing the interaction of the data with redis. This puts a dependency of having redis running while tests are being run (which I'm not against in general). But it adds a dependency that I can see some might recommend handling with stubs.
  • Cristian Bica
    Cristian Bica almost 12 years
    Nope. That's MockRedis is doing ... it stores the data in memory so you don't need a redis server. I said "so you'll be sure that the tests will run on any machine even if the redis server is not installed".
  • Chris Nicola
    Chris Nicola over 11 years
    I believe this is now REDIS.flushdb
  • Paul Pettengill
    Paul Pettengill over 11 years
    One thing you might want to add to the top of this answer, which was very helpful is: add gem 'mock_redis' to your Gemfile and add this line to your spec_helper as well require 'mock_redis'
  • TiSer
    TiSer almost 11 years
    I have "undefined method `stubs' for Redis:Class (NoMethodError)". :(
  • Matt Huggins
    Matt Huggins over 10 years
    @TiSir - Try calling .stub(:new).and_return(redis_instance) instead.
  • Phương Nguyễn
    Phương Nguyễn about 10 years
    I think this is a bad choice to mock redis, which is super fast. A better option is to spin up a test redis server and use it instead. I created a gem that facilitate that: rubygems.org/gems/redis_test
  • efatsi
    efatsi almost 10 years
    I agree that running the tests without needing a redis server is valuable. I'd suggest setting the Resque redis instance to a MockRedis instance instead of stubbing it out though. Resque.redis = MockRedis.new
  • khiav reoy
    khiav reoy almost 5 years
    There are bugs in mock_redis even in 2019. It's a bad choice to use it :(