Module: Protobuf::RSpec::Helpers

Defined in:
lib/protobuf/rspec/helpers.rb

Defined Under Namespace

Classes: ClientMock

Instance Method Summary collapse

Instance Method Details

#call_local_service(klass, method, request, timeout = 5) ⇒ ClientMock Also known as: call_service

Call a local service to test responses and behavior based on the given request. Should use to outside-in test a local RPC Service without testing the underlying socket implementation.

Examples:

Test a local service method

# Implementation
module Proto
  class UserService < Protobuf::Rpc::Service
    def create
      user = User.create_from_proto(request)
      self.response = ProtoRepresenter.new(user).to_proto
    end
  end
end

# Spec
describe Proto::UserService do
  describe '#create' do
    it 'creates a new user' do
      create_request = Proto::UserCreate.new(...)
      client = call_local_service(Proto::UserService, :create, create_request)
      client.response.should eq(some_response_object)
    end
  end
end

Parameters:

  • klass (Class)

    the service class constant

  • method (Symbol, String)

    a symbol or string denoting the method to call

  • request (Protobuf::Message)

    the request message of the expected type for the given method

  • timeout (Fixnum) (defaults to: 5)

    force timeout on service call

Returns:

  • (ClientMock)

    an object which contains the request and potential response or error objects



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/protobuf/rspec/helpers.rb', line 47

def call_local_service(klass, method, request, timeout=5)
  service = klass.new
  response = service.rpcs[method].response_type.new
  service.stub(:request).and_return(request)
  service.stub(:response).and_return(response)
  def service.response= res
    @response = res
    self.stub(:response).and_return(@response)
  end
  def service.rpc_failed message
    @custom_error = message
    send_response
  end
  def service.send_response
    @send_response_called = true
  end

  client_mock = ClientMock.new
  client_mock.request = request

  begin
    yield(service) if block_given?
    service.__send__("rpc_#{method}")
  rescue
    client_mock.error = $!
  else
    client_mock.error = service.instance_variable_get(:@custom_error)
  ensure
    if client_mock.error.nil?
      if service.instance_variable_get(:@async_responder)
        Timeout.timeout(timeout) do
          sleep 0.5 until service.instance_variable_get(:@send_response_called)
        end
      end
      client_mock.response = service.instance_variable_get(:@response) || response
    end
  end

  client_mock
end

#mock_proto(klass, stubs = {}) ⇒ OpenStruct

Stubs out a protobuf message object internals so that we can just test the needed fields. It’s debatable if this is actually helpful since the protobuf message is so lean in the first place.

Parameters:

  • klass (Class, String)

    the message class constant or the message name

  • stubs (Hash) (defaults to: {})

    the stubbed fields and values

Returns:

  • (OpenStruct)

    the mocked message



186
187
188
189
190
191
192
193
# File 'lib/protobuf/rspec/helpers.rb', line 186

def mock_proto(klass, stubs={})
  proto_instance = OpenStruct.new({:mock_name => klass}.merge(stubs))
  proto_instance.stub!(:has_field? => true)
  proto = mock(klass)
  proto.stub!(:tap).and_yield(proto_instance)
  klass.stub!(:new).and_return(proto)
  proto_instance
end

#mock_remote_service(klass, method, cb_mocks = {}) ⇒ Mock Also known as: mock_service

Create a mock service that responds in the way you are expecting to aid in testing client -> service calls. In order to test your success callback you should provide a :response object. Similarly, to test your failure callback you should provide an :error object. If you would like to test the object that is given as a request you should provide a :request object.

Examples:

Testing the client on_success callback

# Method under test
def create_user(request)
  status = 'unknown'
  Proto::UserService.client.create(request) do |c|
    c.on_success do |response|
      status = response.status
    end
  end
  status
end
...

# spec
it 'verifies the on_success method behaves correctly' do
  mock_remote_service(Proto::UserService, :client, response: mock('response_mock', status: 'success'))
  create_user(request).should eq('success')
end

Testing the client on_failure callback

# Method under test
def create_user(request)
  status = nil
  Proto::UserService.client.create(request) do |c|
    c.on_failure do |error|
      status = 'error'
      ErrorReporter.report(error.message)
    end
  end
  status
end
...

# spec
it 'verifies the on_success method behaves correctly' do
  mock_remote_service(Proto::UserService, :client, error: mock('error_mock', message: 'this is an error message'))
  ErrorReporter.should_receive(:report).with('this is an error message')
  create_user(request).should eq('error')
end

Testing the given client request object

# Method under test
def create_user
  request = ... # some operation to build a request on state
  Proto::UserService.client.create(request) do |c|
    ...
  end
end
...

# spec
it 'verifies the request is built correctly' do
  expected_request = ... # some expectation
  mock_remote_service(Proto::UserService, :client, request: expected_request)
  create_user(request)
end

Parameters:

  • klass (Class)

    the service class constant

  • method (Symbol, String)

    a symbol or string denoting the method to call

  • cb_mocks (Hash) (defaults to: {})

    provides expectation objects to invoke on_success (with :response), on_failure (with :error), and the request object (:request)

Returns:

  • (Mock)

    the stubbed out client mock



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/protobuf/rspec/helpers.rb', line 155

def mock_remote_service(klass, method, cb_mocks={})
  cb_mocks = {:error => mock('error', :message => nil, :code => nil)}.merge(cb_mocks)
  klass.stub(:client).and_return(client = mock('Client'))
  client.stub(method).and_yield(client)
  if cb_mocks[:request]
    client.should_receive(method).with(cb_mocks[:request])
  else
    client.should_receive(method)
  end

  if cb_mocks[:response]
    client.stub(:on_success).and_yield(cb_mocks[:response])
  else
    client.stub(:on_success)
  end

  if cb_mocks[:error]
    client.stub(:on_failure).and_yield(cb_mocks[:error])
  else
    client.stub(:on_failure)
  end
  client
end