使用RSpec编写与Rails集成的Redis测试

23

我有一个模型类,将数据缓存到redis中。第一次调用模型方法时,它会计算JSON / Hash值并将其存储在Redis中。在某些情况下,我会“清除”这些数据,然后在下一次调用时重新计算。

这里是类似于我用来将数据存储在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

我应该如何对这段代码进行单元测试?我使用RSpec和Capybara。如果有帮助的话,我还使用Cucumber和Capybara进行集成测试。

3个回答

24

我喜欢在运行测试时同时运行Redis。Redis与PostgreSQL等数据库相比,速度极快,不会明显减慢测试运行时间。

只需确保在before(:each)块或相应的Cucumber hook中调用REDIS.flush即可。

你可以独立地测试data_to_cache而不使用Redis,但除非你完全信任所使用的Redis驱动程序及其提供的合同,否则最好实际测试cache_data(以及相应的缓存获取方法)。这也允许你在没有彻底重写测试的情况下切换到不同的Redis驱动程序(或不同的快速KV存储)。


18
我相信现在应该是 REDIS.flushdb - Chris Nicola

23

首先,在spec_helper.rb文件中添加以下代码,这样您就可以确保测试即使在未安装Redis服务器的任何机器上也能运行(请务必在gemfile中添加test范围下的mock_redis):

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

接下来我会进行测试:

  1. 已写入REDIS的数据是否符合预期
  2. 调用cache_data、flush_data、cache_data序列两次是否会导致data_to_cache函数被调用两次

那么你建议运行redis并实际测试数据与redis的交互。这会导致在运行测试时需要运行redis(我总体上不反对)。但是它增加了一个依赖项,我可以看到有些人建议使用存根来处理。 - Kevin Bedell
15
你可能想在这个回答的开头添加一句话,这将非常有帮助:在你的Gemfile中添加gem 'mock_redis'并在spec_helper文件中加入以下一行代码 require 'mock_redis'。请注意,翻译后的内容保持原意,简洁易懂,不包括任何解释,并且不会提供其他信息。 - Paul Pettengill
2
我遇到了“未定义方法`stubs' for Redis:Class (NoMethodError)”这个问题。:( - TiSer
2
@TiSir - 尝试调用 .stub(:new).and_return(redis_instance) - Matt Huggins
1
我认为模拟 Redis 是一个不好的选择,因为 Redis 本身就非常快。更好的选择是启动一个测试 Redis 服务器并使用它。我创建了一个可以方便地实现这一点的 gem:http://rubygems.org/gems/redis_test - Phương Nguyễn
显示剩余3条评论

7

2022年5月,这两个答案都已经过时。以下是更新版本,并包含一个样例测试。

将以下内容添加到Gemfile文件中:

gem 'mock_redis'

使用以下命令安装mock_redis宝石:

bundle install

在您的spec_helper.rbrails_helper.rb中添加以下内容。
require 'mock_redis'

RSpec.configure do |config|
  '''
  Creating a stub/mock Redis for running the tests without requiring an actual Redis server. This will store the Redis data in memory instead of the Redis server. 
  '''
  config.before(:each) do
    mock_redis = MockRedis.new
    allow(Redis).to receive(:new).and_return(mock_redis)
  end
end

这将确保在所有测试中模拟Redis。
假设您有一个名为module_in_my_app.rb的模块,它使用Redis,如下所示:
module ModuleInMyApp
    extend self
    @@redis = Redis.new(url: "redis://#{ENV["REDIS_URL"]}")

    def get_or_create_my_key(keys, default_value)
       return @@redis.set(key, value)
    end

    def get_my_key(key)
       return @@redis.get(key)
    end
end

您可以在您的规范文件(module_in_my_app_spec.rb)中这样使用它。
describe "test my redis module" do
    it "should set the key" do
        ModuleInMyApp.set_my_key("US", "United States of America")

        redis_var = class_variable_get(:@@redis)
        expect(redis_var.get("US")).to eq("United States of America")
    end

    it "should get the key" do
        mock_redis = Redis.new
        mock_redis.set("CA", "Canada")
        ModuleInMyApp.class_variable_set(:@@redis, mock_redis)

        actual_result = ModuleInMyApp.get_my_key("CA")
        expect(actual_result).to eq("Canada")
    end
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接