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 ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .yardoc
6
+ doc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in protobuf-rspec.gemspec
4
+ gemspec
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,2 @@
1
+ require 'protobuf/rspec/version'
2
+ require 'protobuf/rspec/helpers'
@@ -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,5 @@
1
+ module Protobuf
2
+ module RSpec
3
+ VERSION = "0.0.1"
4
+ end
5
+ 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: