protobuf-rspec 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/README.md +115 -0
- data/Rakefile +1 -0
- data/lib/protobuf/rspec.rb +2 -0
- data/lib/protobuf/rspec/helpers.rb +197 -0
- data/lib/protobuf/rspec/version.rb +5 -0
- data/protobuf-rspec.gemspec +26 -0
- metadata +102 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
protobuf-rspec gem
|
2
|
+
==================
|
3
|
+
|
4
|
+
RSpec Helpers designed to give you mock abstraction of client or service layer. Require as protobuf/rspec/helpers and include into your running RSpec configuration.
|
5
|
+
|
6
|
+
**Note:** Tested to work with the [protobuf gem](https://rubygems.org/gems/protobuf) (>= 1.0).
|
7
|
+
|
8
|
+
Mocking Client Requests (Outside-In test of the service methods)
|
9
|
+
----------------------------------------------------------------
|
10
|
+
|
11
|
+
Use this method to call a local service in your application to test responses and behavior based on the given request. This should be used to outside-in test a local RPC Service without testing the underlying socket implementation or needing actual client code to invoke the method(s) under test.
|
12
|
+
|
13
|
+
Given the service implementation below:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
module Proto
|
17
|
+
class UserService < Protobuf::Rpc::Service
|
18
|
+
def create
|
19
|
+
user = User.create_from_proto(request)
|
20
|
+
self.response = ProtoRepresenter.new(user).to_proto
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
This could be one way to test the implementation while ignoring the RPC backend:
|
27
|
+
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
describe Proto::UserService do
|
31
|
+
describe '#create' do
|
32
|
+
it 'creates a new user' do
|
33
|
+
create_request = Proto::UserCreate.new(...)
|
34
|
+
client = call_local_service(Proto::UserService, :create, create_request)
|
35
|
+
client.response.should eq(some_response_object)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Mocking Service Responses
|
42
|
+
-------------------------
|
43
|
+
|
44
|
+
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.
|
45
|
+
|
46
|
+
### Testing the client on_success callback
|
47
|
+
```ruby
|
48
|
+
# Method under test
|
49
|
+
def create_user(request)
|
50
|
+
status = 'unknown'
|
51
|
+
Proto::UserService.client.create(request) do |c|
|
52
|
+
c.on_success do |response|
|
53
|
+
status = response.status
|
54
|
+
end
|
55
|
+
end
|
56
|
+
status
|
57
|
+
end
|
58
|
+
...
|
59
|
+
|
60
|
+
# spec
|
61
|
+
it 'verifies the on_success method behaves correctly' do
|
62
|
+
mock_remote_service(Proto::UserService, :client, response: mock('response_mock', status: 'success'))
|
63
|
+
create_user(request).should eq('success')
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
### Testing the client on_failure callback
|
68
|
+
```ruby
|
69
|
+
# Method under test
|
70
|
+
def create_user(request)
|
71
|
+
status = nil
|
72
|
+
Proto::UserService.client.create(request) do |c|
|
73
|
+
c.on_failure do |error|
|
74
|
+
status = 'error'
|
75
|
+
ErrorReporter.report(error.message)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
status
|
79
|
+
end
|
80
|
+
...
|
81
|
+
|
82
|
+
# spec
|
83
|
+
it 'verifies the on_success method behaves correctly' do
|
84
|
+
mock_remote_service(Proto::UserService, :client, error: mock('error_mock', message: 'this is an error message'))
|
85
|
+
ErrorReporter.should_receive(:report).with('this is an error message')
|
86
|
+
create_user(request).should eq('error')
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
### Testing the given client request object
|
91
|
+
```ruby
|
92
|
+
# Method under test
|
93
|
+
def create_user
|
94
|
+
request = ... # some operation to build a request on state
|
95
|
+
Proto::UserService.client.create(request) do |c|
|
96
|
+
...
|
97
|
+
end
|
98
|
+
end
|
99
|
+
...
|
100
|
+
|
101
|
+
# spec
|
102
|
+
it 'verifies the request is built correctly' do
|
103
|
+
expected_request = ... # some expectation
|
104
|
+
mock_remote_service(Proto::UserService, :client, request: expected_request)
|
105
|
+
create_user(request)
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
Feedback
|
110
|
+
|
111
|
+
Feedback and comments are welcome:
|
112
|
+
|
113
|
+
Web: [rand9.com](http://rand9.com)
|
114
|
+
Twitter: [@localshred](https://twitter.com/localshred)
|
115
|
+
Github: [github](https://github.com/localshred)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'protobuf/rpc/rpc.pb'
|
2
|
+
|
3
|
+
# RSpec Helpers designed to give you mock abstraction of client or service layer.
|
4
|
+
# Require as protobuf/rspec/helpers and include into your running RSpec configuration.
|
5
|
+
#
|
6
|
+
# @example Configure RSpec to use Protobuf Helpers
|
7
|
+
# require 'protobuf/rspec'
|
8
|
+
# RSpec.configure do |config|
|
9
|
+
# config.include Protobuf::Rspec::Helpers
|
10
|
+
# end
|
11
|
+
module Protobuf
|
12
|
+
module RSpec
|
13
|
+
module Helpers
|
14
|
+
|
15
|
+
ClientMock = Struct.new("ClientMock", :request, :response, :error)
|
16
|
+
|
17
|
+
# Call a local service to test responses and behavior based on the given request.
|
18
|
+
# Should use to outside-in test a local RPC Service without testing the underlying socket implementation.
|
19
|
+
#
|
20
|
+
# @example Test a local service method
|
21
|
+
# # Implementation
|
22
|
+
# module Proto
|
23
|
+
# class UserService < Protobuf::Rpc::Service
|
24
|
+
# def create
|
25
|
+
# user = User.create_from_proto(request)
|
26
|
+
# self.response = ProtoRepresenter.new(user).to_proto
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # Spec
|
32
|
+
# describe Proto::UserService do
|
33
|
+
# describe '#create' do
|
34
|
+
# it 'creates a new user' do
|
35
|
+
# create_request = Proto::UserCreate.new(...)
|
36
|
+
# client = call_local_service(Proto::UserService, :create, create_request)
|
37
|
+
# client.response.should eq(some_response_object)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# @param [Class] klass the service class constant
|
43
|
+
# @param [Symbol, String] method a symbol or string denoting the method to call
|
44
|
+
# @param [Protobuf::Message] request the request message of the expected type for the given method
|
45
|
+
# @param [Fixnum] timeout force timeout on service call
|
46
|
+
# @return [ClientMock] an object which contains the request and potential response or error objects
|
47
|
+
def call_local_service(klass, method, request, timeout=5)
|
48
|
+
service = klass.new
|
49
|
+
response = service.rpcs[method].response_type.new
|
50
|
+
service.stub(:request).and_return(request)
|
51
|
+
service.stub(:response).and_return(response)
|
52
|
+
def service.response= res
|
53
|
+
@response = res
|
54
|
+
self.stub(:response).and_return(@response)
|
55
|
+
end
|
56
|
+
def service.rpc_failed message
|
57
|
+
@custom_error = message
|
58
|
+
send_response
|
59
|
+
end
|
60
|
+
def service.send_response
|
61
|
+
@send_response_called = true
|
62
|
+
end
|
63
|
+
|
64
|
+
client_mock = ClientMock.new
|
65
|
+
client_mock.request = request
|
66
|
+
|
67
|
+
begin
|
68
|
+
yield(service) if block_given?
|
69
|
+
service.__send__("rpc_#{method}")
|
70
|
+
rescue
|
71
|
+
client_mock.error = $!
|
72
|
+
else
|
73
|
+
client_mock.error = service.instance_variable_get(:@custom_error)
|
74
|
+
ensure
|
75
|
+
if client_mock.error.nil?
|
76
|
+
if service.instance_variable_get(:@async_responder)
|
77
|
+
Timeout.timeout(timeout) do
|
78
|
+
sleep 0.5 until service.instance_variable_get(:@send_response_called)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
client_mock.response = service.instance_variable_get(:@response) || response
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
client_mock
|
86
|
+
end
|
87
|
+
alias :call_service :call_local_service
|
88
|
+
|
89
|
+
# Create a mock service that responds in the way you are expecting to aid in testing client -> service calls.
|
90
|
+
# In order to test your success callback you should provide a :response object. Similarly, to test your failure
|
91
|
+
# callback you should provide an :error object. If you would like to test the object that is given as a request
|
92
|
+
# you should provide a :request object.
|
93
|
+
#
|
94
|
+
# @example Testing the client on_success callback
|
95
|
+
# # Method under test
|
96
|
+
# def create_user(request)
|
97
|
+
# status = 'unknown'
|
98
|
+
# Proto::UserService.client.create(request) do |c|
|
99
|
+
# c.on_success do |response|
|
100
|
+
# status = response.status
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
# status
|
104
|
+
# end
|
105
|
+
# ...
|
106
|
+
#
|
107
|
+
# # spec
|
108
|
+
# it 'verifies the on_success method behaves correctly' do
|
109
|
+
# mock_remote_service(Proto::UserService, :client, response: mock('response_mock', status: 'success'))
|
110
|
+
# create_user(request).should eq('success')
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# @example Testing the client on_failure callback
|
114
|
+
# # Method under test
|
115
|
+
# def create_user(request)
|
116
|
+
# status = nil
|
117
|
+
# Proto::UserService.client.create(request) do |c|
|
118
|
+
# c.on_failure do |error|
|
119
|
+
# status = 'error'
|
120
|
+
# ErrorReporter.report(error.message)
|
121
|
+
# end
|
122
|
+
# end
|
123
|
+
# status
|
124
|
+
# end
|
125
|
+
# ...
|
126
|
+
#
|
127
|
+
# # spec
|
128
|
+
# it 'verifies the on_success method behaves correctly' do
|
129
|
+
# mock_remote_service(Proto::UserService, :client, error: mock('error_mock', message: 'this is an error message'))
|
130
|
+
# ErrorReporter.should_receive(:report).with('this is an error message')
|
131
|
+
# create_user(request).should eq('error')
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# @example Testing the given client request object
|
135
|
+
# # Method under test
|
136
|
+
# def create_user
|
137
|
+
# request = ... # some operation to build a request on state
|
138
|
+
# Proto::UserService.client.create(request) do |c|
|
139
|
+
# ...
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
# ...
|
143
|
+
#
|
144
|
+
# # spec
|
145
|
+
# it 'verifies the request is built correctly' do
|
146
|
+
# expected_request = ... # some expectation
|
147
|
+
# mock_remote_service(Proto::UserService, :client, request: expected_request)
|
148
|
+
# create_user(request)
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# @param [Class] klass the service class constant
|
152
|
+
# @param [Symbol, String] method a symbol or string denoting the method to call
|
153
|
+
# @param [Hash] cb_mocks provides expectation objects to invoke on_success (with :response), on_failure (with :error), and the request object (:request)
|
154
|
+
# @return [Mock] the stubbed out client mock
|
155
|
+
def mock_remote_service(klass, method, cb_mocks={})
|
156
|
+
cb_mocks = {:error => mock('error', :message => nil, :code => nil)}.merge(cb_mocks)
|
157
|
+
klass.stub(:client).and_return(client = mock('Client'))
|
158
|
+
client.stub(method).and_yield(client)
|
159
|
+
if cb_mocks[:request]
|
160
|
+
client.should_receive(method).with(cb_mocks[:request])
|
161
|
+
else
|
162
|
+
client.should_receive(method)
|
163
|
+
end
|
164
|
+
|
165
|
+
if cb_mocks[:response]
|
166
|
+
client.stub(:on_success).and_yield(cb_mocks[:response])
|
167
|
+
else
|
168
|
+
client.stub(:on_success)
|
169
|
+
end
|
170
|
+
|
171
|
+
if cb_mocks[:error]
|
172
|
+
client.stub(:on_failure).and_yield(cb_mocks[:error])
|
173
|
+
else
|
174
|
+
client.stub(:on_failure)
|
175
|
+
end
|
176
|
+
client
|
177
|
+
end
|
178
|
+
alias :mock_service :mock_remote_service
|
179
|
+
|
180
|
+
# Stubs out a protobuf message object internals so that we can just test the needed fields.
|
181
|
+
# It's debatable if this is actually helpful since the protobuf message is so lean in the first place.
|
182
|
+
#
|
183
|
+
# @param [Class, String] klass the message class constant or the message name
|
184
|
+
# @param [Hash] stubs the stubbed fields and values
|
185
|
+
# @return [OpenStruct] the mocked message
|
186
|
+
def mock_proto(klass, stubs={})
|
187
|
+
proto_instance = OpenStruct.new({:mock_name => klass}.merge(stubs))
|
188
|
+
proto_instance.stub!(:has_field? => true)
|
189
|
+
proto = mock(klass)
|
190
|
+
proto.stub!(:tap).and_yield(proto_instance)
|
191
|
+
klass.stub!(:new).and_return(proto)
|
192
|
+
proto_instance
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "protobuf/rspec/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "protobuf-rspec"
|
7
|
+
s.version = Protobuf::RSpec::VERSION
|
8
|
+
s.authors = ["BJ Neilsen"]
|
9
|
+
s.email = ["bj.neilsen@gmail.com"]
|
10
|
+
s.homepage = "http://github.com/localshred/protobuf-rspec"
|
11
|
+
s.summary = %q{Protobuf RSpec helpers for testing services and clients. Meant to be used with the protobuf gem. Decouple external services/clients from each other using the given helper methods.}
|
12
|
+
s.description = s.summary
|
13
|
+
|
14
|
+
s.rubyforge_project = "protobuf-rspec"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_runtime_dependency "protobuf", "~> 1.1"
|
23
|
+
s.add_runtime_dependency "rspec", "~> 2.8"
|
24
|
+
s.add_development_dependency "yard", "~> 0.7"
|
25
|
+
s.add_development_dependency "redcarpet", "~> 2.1"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: protobuf-rspec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- BJ Neilsen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-10 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: protobuf
|
16
|
+
requirement: &2160846800 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.1'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2160846800
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &2160845600 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.8'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2160845600
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: yard
|
38
|
+
requirement: &2160844920 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.7'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2160844920
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: redcarpet
|
49
|
+
requirement: &2160843920 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.1'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2160843920
|
58
|
+
description: Protobuf RSpec helpers for testing services and clients. Meant to be
|
59
|
+
used with the protobuf gem. Decouple external services/clients from each other using
|
60
|
+
the given helper methods.
|
61
|
+
email:
|
62
|
+
- bj.neilsen@gmail.com
|
63
|
+
executables: []
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- .gitignore
|
68
|
+
- Gemfile
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- lib/protobuf/rspec.rb
|
72
|
+
- lib/protobuf/rspec/helpers.rb
|
73
|
+
- lib/protobuf/rspec/version.rb
|
74
|
+
- protobuf-rspec.gemspec
|
75
|
+
homepage: http://github.com/localshred/protobuf-rspec
|
76
|
+
licenses: []
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project: protobuf-rspec
|
95
|
+
rubygems_version: 1.8.10
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: Protobuf RSpec helpers for testing services and clients. Meant to be used
|
99
|
+
with the protobuf gem. Decouple external services/clients from each other using
|
100
|
+
the given helper methods.
|
101
|
+
test_files: []
|
102
|
+
has_rdoc:
|