protobuf-rspec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: