RSpec and ZeroMQ

By Morten Møller Riis

December 19 2011 14:30 CET

When you have something like this using ZeroMQ:

client.rb

               class Client
                 def self.listen
                   socket = ZMQ::Context.new.socket(ZMQ::UPSTREAM)
                   socket.bind("tcp://127.0.0.1:1234")
            
                   while(true)
                     msg = socket.recv()
                     if msg == "quit"
                       socket.close
                       return
                     end
                     ...
                   end
             

And you would like to write a spec for it you will run into the problem that #recv() is a blocking method.

How best to go about this? I would prefer some kind of spawning a seperate process but that get’s kinda hairy.

Instead I have opted to simply mock it and write the following two methods:

spec_helper.rb

               def queue_zmq_message(msg)
                 @zmq_queue ||= []
                 @zmq_queue << msg
               end
            
               def mock_zmq
                 @zmq_queue ||= []
                 zmq_socket = mock('zmq_socket')
                 zmq_socket.stub(:bind)
                 zmq_socket.stub(:recv).and_return do
                   queue_zmq_message("quit") if @zmq_queue.empty?
                   @zmq_queue.shift
                 end
            
                 zmq_context = mock('zmq_context')
                 zmq_context.stub(:socket).and_return(zmq_socket)
                 ZMQ::Context.stub(:new).and_return(zmq_context)
                 zmq_socket.stub(:close)
               end
             

This allows me to do something like this in my specs:

client_spec.rb

               before do
                mock_zmq
               end
               
               it "should do something when a message is received" do      
                 queue_zmq_message "some message"
                 # Expections here
                 Client.listen
                 # ...or expections here
               end
             

This is a bit simplified from the project I am using it in now. But you should get the point. The program accepts some sort of quit signal as well to break us out of the main loop.