protobuf-rspec 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +114 -27
- data/lib/protobuf/rspec/helpers.rb +66 -40
- data/lib/protobuf/rspec/version.rb +1 -1
- metadata +13 -13
data/README.md
CHANGED
@@ -1,51 +1,128 @@
|
|
1
1
|
protobuf-rspec gem
|
2
2
|
==================
|
3
3
|
|
4
|
-
RSpec Helpers designed to give you mock abstraction of client or service layer. Require as protobuf/rspec
|
4
|
+
RSpec Helpers designed to give you mock abstraction of client or service layer. Require as `protobuf/rspec` and include into your running RSpec configuration.
|
5
5
|
|
6
|
-
|
6
|
+
```ruby
|
7
|
+
# spec_helper.rb
|
8
|
+
# ...
|
9
|
+
|
10
|
+
require 'protobuf/rspec'
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.include Protobuf::RSpec::Helpers
|
13
|
+
end
|
14
|
+
```
|
15
|
+
|
16
|
+
**Note:** Tested to work with the [protobuf gem](https://rubygems.org/gems/protobuf) (>= 2.x).
|
17
|
+
|
18
|
+
Unit-Testing Service Behavior
|
19
|
+
-----------------------------
|
7
20
|
|
8
|
-
|
9
|
-
----------------------------------------------------------------
|
21
|
+
### `local_rpc`
|
10
22
|
|
11
|
-
|
23
|
+
To unit test your service you should use the `local_rpc` helper method. `local_rpc` helps you call the service instance method of your choosing to ensure that the correct responses are generated with the given requests. 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 endpoint method under test.
|
12
24
|
|
13
25
|
Given the service implementation below:
|
14
26
|
|
15
27
|
```ruby
|
16
|
-
module
|
28
|
+
module Services
|
17
29
|
class UserService < Protobuf::Rpc::Service
|
18
30
|
def create
|
19
|
-
|
20
|
-
|
31
|
+
if request.name
|
32
|
+
user = User.create_from_proto(request)
|
33
|
+
respond_with(user)
|
34
|
+
else
|
35
|
+
rpc_failed 'Error: name required'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify
|
40
|
+
user = User.find_by_guid(request.guid)
|
41
|
+
if user
|
42
|
+
Resque.enqueue(EmailUserJob, user.id)
|
43
|
+
respond_with(:queued => true)
|
44
|
+
else
|
45
|
+
rpc_failed 'Error: user not found'
|
46
|
+
end
|
21
47
|
end
|
22
48
|
end
|
23
49
|
end
|
24
50
|
```
|
25
51
|
|
26
|
-
|
27
|
-
|
52
|
+
Specs that test these two methods and their various cases could look something like this:
|
28
53
|
|
29
54
|
```ruby
|
30
|
-
describe
|
55
|
+
describe Services::UserService do
|
31
56
|
describe '#create' do
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
57
|
+
subject { local_rpc(:create, request) }
|
58
|
+
|
59
|
+
context 'when request is valid' do
|
60
|
+
let(:request) { { :name => 'Jack' } }
|
61
|
+
let(:user_mock) { FactoryGirl.build(:user) }
|
62
|
+
before { User.should_receive(:create_from_proto).and_return(user_mock) }
|
63
|
+
it { should eq(user_mock) }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when name is not given' do
|
67
|
+
let(:request) { :name => '' }
|
68
|
+
it { should =~ /Error/ }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#notify' do
|
73
|
+
let(:request) { { :guid => 'USR-123' } }
|
74
|
+
let(:user_mock) { FactoryGirl.build(:user) }
|
75
|
+
subject { local_rpc(:notify, request) }
|
76
|
+
|
77
|
+
context 'when user is found' do
|
78
|
+
before { User.should_receive(:find_by_guid).with(request.guid).and_return(user_mock) }
|
79
|
+
before { Resqueue.should_receive(:enqueue).with(EmailUserJob, request.guid)
|
80
|
+
its(:queued) { should be_true }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when user is not found' do
|
84
|
+
before { Resque.should_not_receive(:enqueue) }
|
85
|
+
it { should =~ /Error/ }
|
36
86
|
end
|
37
87
|
end
|
38
88
|
end
|
39
89
|
```
|
40
90
|
|
91
|
+
### `subject_service`
|
92
|
+
|
93
|
+
One thing to note is that `local_rpc` uses `described_class` as the class to invoke for the given method. If you need to instead test a different class than your `described_class`, simply pass a block to `subject_service` which returns the class you would like to use instead.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
describe 'The User Service' do
|
97
|
+
subject_service { Services::UserService }
|
98
|
+
|
99
|
+
describe '#create' do
|
100
|
+
subject { local_rpc(:create, request) }
|
101
|
+
# ...
|
102
|
+
end
|
103
|
+
|
104
|
+
#...
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
### `request_class` and `response_class`
|
109
|
+
|
110
|
+
Both the `request_class` and `response_class` helper methods will return the class type for, you guessed it, the request and response type defined by the service method. This can aid in setting up the correct objects for expectations. Simply pass in the name of the endpoint you are testing to get the appropriate message class.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
request_class(:create) # => UserCreateRequest
|
114
|
+
response_class(:create) # => User
|
115
|
+
```
|
41
116
|
Mocking Service Responses
|
42
117
|
-------------------------
|
43
118
|
|
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 `:
|
119
|
+
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 `:success` option. To test your failure callback you should provide a `:failure` option.
|
120
|
+
|
121
|
+
|
122
|
+
### Testing the client `on_success` callback
|
45
123
|
|
46
|
-
|
124
|
+
Passing a `:success` key as an option to `mock_rpc` will cause the `on_success` callback to be invoked with the given object. In this way you can simulate a successful service response to verify that you are handling the response appropriately. You can alternatively use the `:response` key to invoke the `on_success` block.
|
47
125
|
|
48
|
-
### Testing the client on_success callback
|
49
126
|
```ruby
|
50
127
|
# Method under test
|
51
128
|
def create_user(request)
|
@@ -58,15 +135,19 @@ Asserting the request object can be done one of two ways: direct or explicit. If
|
|
58
135
|
status
|
59
136
|
end
|
60
137
|
...
|
61
|
-
|
138
|
+
|
62
139
|
# spec
|
63
140
|
it 'verifies the on_success method behaves correctly' do
|
64
|
-
|
141
|
+
response_mock = mock('response_mock', :status => 'success')
|
142
|
+
mock_rpc(Proto::UserService, :client, :success => response_mock) # alternatively can use :response key here
|
65
143
|
create_user(request).should eq('success')
|
66
144
|
end
|
67
145
|
```
|
68
146
|
|
69
|
-
### Testing the client on_failure callback
|
147
|
+
### Testing the client `on_failure` callback
|
148
|
+
|
149
|
+
Passing a `:failure` key as an option to `mock_rpc` will cause the `on_failure` callback to be invoked with the given object. In this way you can simulate a service failure and verify you are handling that failure appropriately. You can alternatively use the `:error` key to invoke the `on_failure` block.
|
150
|
+
|
70
151
|
```ruby
|
71
152
|
# Method under test
|
72
153
|
def create_user(request)
|
@@ -83,13 +164,17 @@ end
|
|
83
164
|
|
84
165
|
# spec
|
85
166
|
it 'verifies the on_success method behaves correctly' do
|
86
|
-
|
87
|
-
|
167
|
+
error_mock = mock('error_mock', :message => 'this is an error message')
|
168
|
+
mock_rpc(Proto::UserService, :client, :failure => error_mock) # alternatively can use :error key here
|
169
|
+
ErrorReporter.should_receive(:report).with(error_mock.message)
|
88
170
|
create_user(request).should eq('error')
|
89
171
|
end
|
90
172
|
```
|
91
173
|
|
92
174
|
### Testing the given client request object (direct assert)
|
175
|
+
|
176
|
+
In order to test the request object sent to the service you can pass a `:request` key whose value will be asserted with RSpec's `with` constraint paired with the `should_receive` assertion. Also note that if a `:request` option is given, the assert block will be ignored (see below).
|
177
|
+
|
93
178
|
```ruby
|
94
179
|
# Method under test
|
95
180
|
def create_user
|
@@ -103,12 +188,15 @@ end
|
|
103
188
|
# spec
|
104
189
|
it 'verifies the request is built correctly' do
|
105
190
|
expected_request = ... # some expectation
|
106
|
-
|
191
|
+
mock_rpc(Proto::UserService, :client, :request => expected_request)
|
107
192
|
create_user(request)
|
108
193
|
end
|
109
194
|
```
|
110
195
|
|
111
|
-
### Testing the given client request object (
|
196
|
+
### Testing the given client request object (block assert)
|
197
|
+
|
198
|
+
You can also pass a block to `mock_rpc` which will be yielded the request object. This allows more fine-grained assertions on the request object. Also note that if a `:request` option is given (see above), the assert block will be ignored.
|
199
|
+
|
112
200
|
```ruby
|
113
201
|
# Method under test
|
114
202
|
def create_user
|
@@ -121,7 +209,7 @@ end
|
|
121
209
|
|
122
210
|
# spec
|
123
211
|
it 'verifies the request is built correctly' do
|
124
|
-
|
212
|
+
mock_rpc(Proto::UserService, :client) do |given_request|
|
125
213
|
given_request.field1.should eq 'rainbows'
|
126
214
|
given_request.field2.should eq 'ponies'
|
127
215
|
end
|
@@ -134,6 +222,5 @@ Feedback
|
|
134
222
|
|
135
223
|
Feedback and comments are welcome:
|
136
224
|
|
137
|
-
Web: [rand9.com](http://rand9.com)
|
138
225
|
Twitter: [@localshred](https://twitter.com/localshred)
|
139
226
|
Github: [github](https://github.com/localshred)
|
@@ -22,7 +22,8 @@ module Protobuf
|
|
22
22
|
module ClassMethods
|
23
23
|
|
24
24
|
# Set the service subject. Use this method when the described_class is
|
25
|
-
# not the class you wish to use with methods like local_rpc
|
25
|
+
# not the class you wish to use with methods like local_rpc,
|
26
|
+
# request_class, or response_class.
|
26
27
|
#
|
27
28
|
# @example Override subject service for local_rpc calls
|
28
29
|
# describe Foo::BarService do
|
@@ -33,13 +34,6 @@ module Protobuf
|
|
33
34
|
# its('response.records') { should have(3).items }
|
34
35
|
# end
|
35
36
|
#
|
36
|
-
# @example Override subject service for remote_rpc mocks
|
37
|
-
# describe BarController do
|
38
|
-
# describe '#index' do
|
39
|
-
# subject_service { Foo::BarService }
|
40
|
-
# subject { remote_rpc(:find, request, response) }
|
41
|
-
# end
|
42
|
-
# end
|
43
37
|
#
|
44
38
|
def subject_service
|
45
39
|
if block_given?
|
@@ -62,34 +56,61 @@ module Protobuf
|
|
62
56
|
#
|
63
57
|
# @example Test a local service method
|
64
58
|
# # Implementation
|
65
|
-
# module
|
59
|
+
# module Services
|
66
60
|
# class UserService < Protobuf::Rpc::Service
|
67
61
|
# def create
|
68
|
-
# user = User.create_from_proto(request)
|
69
62
|
# if request.name
|
70
|
-
#
|
63
|
+
# user = User.create_from_proto(request)
|
64
|
+
# respond_with(user)
|
71
65
|
# else
|
72
66
|
# rpc_failed 'Error: name required'
|
73
67
|
# end
|
74
68
|
# end
|
69
|
+
#
|
70
|
+
# def notify
|
71
|
+
# user = User.find_by_guid(request.guid)
|
72
|
+
# if user
|
73
|
+
# Resque.enqueue(EmailUserJob, user.id)
|
74
|
+
# respond_with(:queued => true)
|
75
|
+
# else
|
76
|
+
# rpc_failed 'Error: user not found'
|
77
|
+
# end
|
78
|
+
# end
|
75
79
|
# end
|
76
80
|
# end
|
77
81
|
#
|
78
82
|
# # Spec
|
79
|
-
# describe
|
83
|
+
# describe Services::UserService do
|
80
84
|
# describe '#create' do
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
+
# subject { local_rpc(:create, request) }
|
86
|
+
#
|
87
|
+
# context 'when request is valid' do
|
88
|
+
# let(:request) { { :name => 'Jack' } }
|
89
|
+
# let(:user_mock) { FactoryGirl.build(:user) }
|
90
|
+
# before { User.should_receive(:create_from_proto).and_return(user_mock) }
|
91
|
+
# it { should eq(user_mock) }
|
85
92
|
# end
|
86
93
|
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
94
|
+
# context 'when name is not given' do
|
95
|
+
# let(:request) { :name => '' }
|
96
|
+
# it { should =~ /Error/ }
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# describe '#notify' do
|
101
|
+
# let(:request) { { :guid => 'USR-123' } }
|
102
|
+
# let(:user_mock) { FactoryGirl.build(:user) }
|
103
|
+
# subject { local_rpc(:notify, request) }
|
104
|
+
#
|
105
|
+
# context 'when user is found' do
|
106
|
+
# before { User.should_receive(:find_by_guid).with(request.guid).and_return(user_mock) }
|
107
|
+
# before { Resqueue.should_receive(:enqueue).with(EmailUserJob, request.guid)
|
108
|
+
# its(:queued) { should be_true }
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# context 'when user is not found' do
|
112
|
+
# before { Resque.should_not_receive(:enqueue) }
|
113
|
+
# it { should =~ /Error/ }
|
93
114
|
# end
|
94
115
|
# end
|
95
116
|
# end
|
@@ -98,13 +119,12 @@ module Protobuf
|
|
98
119
|
# @param [Protobuf::Message or Hash] request the request message of the expected type for the given method.
|
99
120
|
# @return [Protobuf::Message or String] the resulting protobuf message or error string
|
100
121
|
def local_rpc(rpc_method, request)
|
101
|
-
|
122
|
+
request_klass = request_class(rpc_method)
|
123
|
+
request = request_klass.new(request) if request.is_a?(Hash)
|
102
124
|
|
103
|
-
outer_request_params = {
|
104
|
-
|
105
|
-
|
106
|
-
:request_proto => request.serialize_to_string
|
107
|
-
}
|
125
|
+
outer_request_params = { :service_name => subject_service.to_s,
|
126
|
+
:method_name => rpc_method.to_s,
|
127
|
+
:request_proto => request.serialize_to_string }
|
108
128
|
|
109
129
|
outer_request = ::Protobuf::Socketrpc::Request.new(outer_request_params)
|
110
130
|
dispatcher = ::Protobuf::Rpc::ServiceDispatcher.new(outer_request)
|
@@ -140,7 +160,8 @@ module Protobuf
|
|
140
160
|
#
|
141
161
|
# # spec
|
142
162
|
# it 'verifies the on_success method behaves correctly' do
|
143
|
-
#
|
163
|
+
# response_mock = mock('response_mock', :status => 'success')
|
164
|
+
# mock_rpc(Proto::UserService, :client, :response => response_mock)
|
144
165
|
# create_user(request).should eq('success')
|
145
166
|
# end
|
146
167
|
#
|
@@ -160,8 +181,9 @@ module Protobuf
|
|
160
181
|
#
|
161
182
|
# # spec
|
162
183
|
# it 'verifies the on_success method behaves correctly' do
|
163
|
-
#
|
164
|
-
#
|
184
|
+
# error_mock = mock('error_mock', :message => 'this is an error message')
|
185
|
+
# mock_rpc(Proto::UserService, :client, :error => error_mock)
|
186
|
+
# ErrorReporter.should_receive(:report).with(error_mock.message)
|
165
187
|
# create_user(request).should eq('error')
|
166
188
|
# end
|
167
189
|
#
|
@@ -178,11 +200,11 @@ module Protobuf
|
|
178
200
|
# # spec
|
179
201
|
# it 'verifies the request is built correctly' do
|
180
202
|
# expected_request = ... # some expectation
|
181
|
-
# mock_rpc(Proto::UserService, :client, request
|
203
|
+
# mock_rpc(Proto::UserService, :client, :request => expected_request)
|
182
204
|
# create_user(request)
|
183
205
|
# end
|
184
206
|
#
|
185
|
-
# @example Testing the given client request object (
|
207
|
+
# @example Testing the given client request object (block assert)
|
186
208
|
# # Method under test
|
187
209
|
# def create_user
|
188
210
|
# request = ... # some operation to build a request on state
|
@@ -204,26 +226,30 @@ module Protobuf
|
|
204
226
|
# @param [Class] klass the service class constant
|
205
227
|
# @param [Symbol, String] method a symbol or string denoting the method to call
|
206
228
|
# @param [Hash] callbacks provides expectation objects to invoke on_success (with :response), on_failure (with :error), and the request object (:request)
|
207
|
-
# @param [Block]
|
229
|
+
# @param [Block] optional. When given, will be invoked with the request message sent to the client method
|
208
230
|
# @return [Mock] the stubbed out client mock
|
209
|
-
def mock_rpc(klass, method, callbacks={}
|
231
|
+
def mock_rpc(klass, method, callbacks = {})
|
210
232
|
client = double('Client', :on_success => true, :on_failure => true)
|
211
233
|
client.stub(method).and_yield(client)
|
212
234
|
|
213
235
|
klass.stub(:client).and_return(client)
|
214
236
|
|
215
|
-
|
237
|
+
case
|
238
|
+
when callbacks[:request] then
|
216
239
|
client.should_receive(method).with(callbacks[:request])
|
217
|
-
|
240
|
+
when block_given? then
|
218
241
|
client.should_receive(method) do |given_req|
|
219
|
-
|
242
|
+
yield(given_req)
|
220
243
|
end
|
221
244
|
else
|
222
245
|
client.should_receive(method)
|
223
246
|
end
|
224
247
|
|
225
|
-
|
226
|
-
client.stub(:
|
248
|
+
success = callbacks[:success] || callbacks[:response]
|
249
|
+
client.stub(:on_success).and_yield(success) unless success.nil?
|
250
|
+
|
251
|
+
failure = callbacks[:failure] || callbacks[:error]
|
252
|
+
client.stub(:on_failure).and_yield(failure) unless failure.nil?
|
227
253
|
|
228
254
|
client
|
229
255
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protobuf-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-11-20 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: protobuf
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152903460 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '2.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152903460
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &2152902320 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '2.8'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2152902320
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &2152900400 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2152900400
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: yard
|
49
|
-
requirement: &
|
49
|
+
requirement: &2152899620 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0.7'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *2152899620
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: redcarpet
|
60
|
-
requirement: &
|
60
|
+
requirement: &2152898740 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '2.1'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *2152898740
|
69
69
|
description: Protobuf RSpec helpers for testing services and clients. Meant to be
|
70
70
|
used with the protobuf gem. Decouple external services/clients from each other using
|
71
71
|
the given helper methods.
|
@@ -97,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
97
|
version: '0'
|
98
98
|
segments:
|
99
99
|
- 0
|
100
|
-
hash: -
|
100
|
+
hash: -623885674851921765
|
101
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
102
|
none: false
|
103
103
|
requirements:
|
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
106
|
version: '0'
|
107
107
|
segments:
|
108
108
|
- 0
|
109
|
-
hash: -
|
109
|
+
hash: -623885674851921765
|
110
110
|
requirements: []
|
111
111
|
rubyforge_project: protobuf-rspec
|
112
112
|
rubygems_version: 1.8.15
|