rspec: How to test redis#subscribe code?

2019-07-17 03:27发布

问题:

Given something like:

class MyClass
  def subscribe
    $redis.subscribe('channel') do |on|
       on.message do |channel, msg|
         Something.create(msg)
       end
    end
  end
end

How can I test that when MyClass executes subscribe, it will run Something.create for each message it receives on the channel?

回答1:

This code you have, it's not very testable. First of all, absolutely get rid of this global $redis variable. Instead, accept an instance of redis in the constructor.

class MyClass
  attr_reader :redis

  def initialize(redis)
    @redis = redis
  end

  def subscribe
    redis.subscribe('channel') do |on|
       on.message do |channel, msg|
         Something.create(msg)
       end
    end
  end
end

Then in tests you can make a dummy redis that you can totally control but which conforms to the api you're using. Something along these lines:

class DummyRedis
  def subscribe(&block)
    @block = block
  end

  def trigger_on
    @block.call make_on_message
  end
end


fake_redis = DummyRedis.new

expect {
  mc = MyClass.new(fake_redis)
  mc.subscribe
  fake_redis.trigger_on
}.to change{Something.count}.from(0).to(1)

This cool technique is called Dependency Injection (or, as some people put it, "passing parameters to constructors").



回答2:

Although this approach is not using actual tests, i would do the following and check the logs.

class MyClass
  def subscribe
    $redis.subscribe('channel') do |on|
       on.message do |channel, msg|
         event = Something.create(msg)
         p event.persisted? ? "success" : "fail"
         p event
       end
    end
  end
end


回答3:

Well, it could be as easy as

describe MyClass do 
  it 'should create something' do 
    expect(Something).to receive(:create)
    subject.subscribe

    subject.trigger_message # you should trigger a message somehow
  end
end