override 0.0.8

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.
Files changed (6) hide show
  1. data/LICENSE +19 -0
  2. data/README.markdown +111 -0
  3. data/Rakefile +35 -0
  4. data/lib/override.rb +21 -0
  5. data/test/all_test.rb +241 -0
  6. metadata +66 -0
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Michel Martens
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,111 @@
1
+ Override
2
+ ============
3
+
4
+ The as-simple-as-possible-but-not-simpler stubbing library.
5
+
6
+ Description
7
+ -----------
8
+
9
+ Override is the essence of the stubbing concept: it takes an object,
10
+ a hash of methods/results, and proceeds to rewrite each method in the
11
+ object. It can be used as a stubbing strategy in most cases, and I'd
12
+ say that cases that don't fit this pattern have a very bad code smell,
13
+ because are either dealing with internals or with side effects.
14
+
15
+ Usage
16
+ -----
17
+
18
+ require 'override'
19
+
20
+ include Override
21
+
22
+ user = User.spawn
23
+ override(user, :name => "Foobar", :email => "foobar@example.org")
24
+ override(User, :find => user)
25
+
26
+ Or alternatively:
27
+
28
+ override(User, :find => override(User.spawn, :name => "Foobar, :email => "foobar@example.org"))
29
+
30
+ You can also send lambdas that will become the body of the redefined method:
31
+
32
+ user = User.spawn :name => "Foobar"
33
+ override(User, :find => lambda { |id| raise ArgumentError unless id == 1; user })
34
+
35
+ And then, in your tests:
36
+
37
+ assert_raise ArgumentError do
38
+ User.find(2)
39
+ end
40
+
41
+ assert_nothing_raised do
42
+ User.find(1)
43
+ end
44
+
45
+ assert_equal "Foobar", User.find(1).name
46
+
47
+ In case you don't know what spawn means, check my library [Spawner](http://github.com/soveran/spawner).
48
+
49
+ It is a common pattern to set expectations for method calls. You can do
50
+ it with the `expect` function:
51
+
52
+ user = User.spawn :name => "Foobar"
53
+ expect(User, :find, :with => [:first, { :include => :friendships }], :return => user)
54
+
55
+ And then:
56
+
57
+ assert_equal "Foobar", User.find(:first, :include => :friendships).name
58
+
59
+ This kind of tests encourage a very fine grained development
60
+ style. Testing side effects is possible with this and with many other
61
+ libraries, but it's something that should be avoided as much as
62
+ possible. Always keep in mind that a deterministic function is the
63
+ easiest to test, so the less coupling there is in the system, the more
64
+ reliable it becomes.
65
+
66
+ Note that this way of setting expectations doesn't count the number
67
+ of calls received by the redefined method. The RSpec equivalent of
68
+ `User.should_receive(:find).with(:first, :include => :friendships)`
69
+ triggers an exception if the method is not called. While it is a handy
70
+ feature, it encourages coupling and testing internals, so my advice
71
+ would be to use it scarcely and to try to refactor your code so it
72
+ doesn't follow this testing anti-pattern. Check the tests for more
73
+ examples.
74
+
75
+ Installation
76
+ ------------
77
+
78
+ $ gem sources -a http://gems.github.com (you only have to do this once)
79
+ $ sudo gem install soveran-override
80
+
81
+ Thanks
82
+ ------
83
+
84
+ Thanks to Tim Goh for his advice of using a hash for rewriting multiple
85
+ methods at once.
86
+
87
+ License
88
+ -------
89
+
90
+ Copyright (c) 2009 Michel Martens
91
+
92
+ Permission is hereby granted, free of charge, to any person
93
+ obtaining a copy of this software and associated documentation
94
+ files (the "Software"), to deal in the Software without
95
+ restriction, including without limitation the rights to use,
96
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
97
+ copies of the Software, and to permit persons to whom the
98
+ Software is furnished to do so, subject to the following
99
+ conditions:
100
+
101
+ The above copyright notice and this permission notice shall be
102
+ included in all copies or substantial portions of the Software.
103
+
104
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
105
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
106
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
107
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
108
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
109
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
110
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
111
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+ require 'rake/clean'
5
+
6
+ gem_spec_file = 'override.gemspec'
7
+
8
+ gem_spec = eval(File.read(gem_spec_file)) rescue nil
9
+
10
+ task :default => :test
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = false
15
+ end
16
+
17
+ Rake::GemPackageTask.new(gem_spec) do |pkg|
18
+ pkg.need_zip = false
19
+ pkg.need_tar = false
20
+ rm_f FileList['pkg/**/*.*']
21
+ end if gem_spec
22
+
23
+ desc "Generate the gemspec file."
24
+ task :gemspec do
25
+ require 'erb'
26
+
27
+ File.open(gem_spec_file, 'w') do |f|
28
+ f.write ERB.new(File.read("#{gem_spec_file}.erb")).result(binding)
29
+ end
30
+ end
31
+
32
+ desc "Builds and installs the gem."
33
+ task :install => :repackage do
34
+ `sudo gem install pkg/#{gem_spec.name}-#{gem_spec.version}.gem`
35
+ end
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "metaid"
3
+
4
+ module Override
5
+ def override object, methods
6
+ methods.each do |method, result|
7
+ result.respond_to?(:to_proc) ?
8
+ object.meta_def(method, &result.to_proc) :
9
+ object.meta_def(method) { |*_| result }
10
+ end
11
+ object
12
+ end
13
+
14
+ def expect object, method, options
15
+ expectation = lambda do |*params|
16
+ raise ArgumentError unless params == options[:with]
17
+ options[:return]
18
+ end
19
+ override(object, method => expectation)
20
+ end
21
+ end
@@ -0,0 +1,241 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'contest'
4
+
5
+ require File.dirname(__FILE__) + "/../lib/override"
6
+
7
+ class Foo
8
+ def bar
9
+ "Bar"
10
+ end
11
+
12
+ def baz
13
+ yield "Baz"
14
+ end
15
+
16
+ def qux a, b, c
17
+ "Qux"
18
+ end
19
+
20
+ def nom
21
+ Bar.foo(1, 2, 3)
22
+ true
23
+ end
24
+
25
+ def == other
26
+ bar == other.bar
27
+ end
28
+ end
29
+
30
+ class Bar
31
+ def self.foo(a, b, c)
32
+ "Bar/Foo"
33
+ end
34
+ end
35
+
36
+ class Callable
37
+ def to_proc
38
+ lambda do |name|
39
+ "Hello #{name}"
40
+ end
41
+ end
42
+ end
43
+
44
+ class TestOverride < Test::Unit::TestCase
45
+ include Override
46
+
47
+ context "dealing with arguments" do
48
+ setup do
49
+ @foo = Foo.new
50
+ override(@foo, :bar => "Hello")
51
+ end
52
+
53
+ should "work without arguments" do
54
+ assert_equal "Hello", @foo.bar
55
+ end
56
+
57
+ should "discard arguments" do
58
+ assert_equal "Hello", @foo.bar(1)
59
+ end
60
+ end
61
+
62
+ context "accepting different return values" do
63
+ setup do
64
+ @foo = Foo.new
65
+ @foo2 = Foo.new
66
+ end
67
+
68
+ should "work for string returns" do
69
+ override(@foo, :bar => "Hello")
70
+ assert_equal "Hello", @foo.bar
71
+ end
72
+
73
+ should "work for numeric returns" do
74
+ override(@foo, :bar => 23)
75
+ assert_equal 23, @foo.bar
76
+ end
77
+
78
+ should "work for object returns" do
79
+ override(@foo, :bar => @foo2)
80
+ assert_equal @foo2, @foo.bar
81
+ end
82
+ end
83
+
84
+ context "working with methods that acepted attributes or blocks" do
85
+ setup do
86
+ @foo = Foo.new
87
+ end
88
+
89
+ should "work for methods that used to receive blocks" do
90
+ override(@foo, :baz => "Hey!")
91
+ assert_equal "Hey!", @foo.baz { |x| x }
92
+ end
93
+
94
+ should "work for methods that used to receive arguments" do
95
+ override(@foo, :qux => "Yay!")
96
+ assert_equal "Yay!", @foo.qux(1, 2, 3)
97
+ end
98
+ end
99
+
100
+ context "rewriting multiple methods at once" do
101
+ should "override all the passed methods" do
102
+ override(@foo, :bar => 1, :baz => 2, :qux => 3)
103
+ assert_equal 1, @foo.bar
104
+ assert_equal 2, @foo.baz
105
+ assert_equal 3, @foo.qux
106
+ end
107
+ end
108
+
109
+ context "chaining successive calls" do
110
+ should "return the object and allow chained calls" do
111
+ assert_equal 1, override(@foo, :bar => 1).bar
112
+ end
113
+ end
114
+
115
+ context "dealing with a proc as the result value" do
116
+ should "uses the proc as the body of the method" do
117
+ override(@foo, :bar => lambda { |name| "Hello #{name}" })
118
+ assert_equal "Hello World", @foo.bar("World")
119
+ end
120
+ end
121
+
122
+ context "using assert raise with lambdas" do
123
+ setup do
124
+ class User
125
+ def name
126
+ "Michel"
127
+ end
128
+ end
129
+
130
+ user = User.new
131
+ override(@foo, :bar => lambda { |id| raise ArgumentError unless id == 1; user })
132
+ end
133
+
134
+ should "lambdas should be able to raise exceptions" do
135
+ assert_raise ArgumentError do
136
+ @foo.bar(2)
137
+ end
138
+
139
+ assert_nothing_raised do
140
+ @foo.bar(1)
141
+ end
142
+
143
+ assert_equal "Michel", @foo.bar(1).name
144
+ end
145
+ end
146
+
147
+ context "documenting a gotcha with lambdas" do
148
+ setup do
149
+ name = "Michel"
150
+ @name = "Michel"
151
+ override(@foo, :bar => lambda { name })
152
+ override(@foo, :baz => lambda { @name })
153
+ end
154
+
155
+ should "succeeds when the lambda returns a local variable" do
156
+ assert_equal "Michel", @foo.bar
157
+ end
158
+
159
+ should "fails when the lambda is supposed to return an instance variable" do
160
+ assert_equal nil, @foo.baz
161
+ end
162
+ end
163
+
164
+ context "using objects that respond to to_proc" do
165
+ setup do
166
+ override(@foo, :bar => Callable.new)
167
+ end
168
+
169
+ should "coerces the value into a proc" do
170
+ assert_equal "Hello World", @foo.bar("World")
171
+ end
172
+ end
173
+
174
+ context "supporting procs as return values" do
175
+ setup do
176
+ override(@foo, :bar => lambda { lambda { "Hey!" } })
177
+ end
178
+
179
+ should "returns a lambda if it's wraped inside a proc" do
180
+ assert_equal "Hey!", @foo.bar.call
181
+ end
182
+ end
183
+
184
+ context "setting expectations" do
185
+ setup do
186
+ expect(@foo, :bar, :with => ["Michel", 32], :return => true)
187
+ end
188
+
189
+ should "raises an error if expectations are not met" do
190
+ assert_raise ArgumentError do
191
+ @foo.bar "Michel", 31
192
+ end
193
+ end
194
+
195
+ should "succeeds if expectations are met" do
196
+ assert_nothing_raised do
197
+ @foo.bar "Michel", 32
198
+ end
199
+ end
200
+ end
201
+
202
+ context "setting expectations with hashes in the param list" do
203
+ setup do
204
+ expect(@foo, :bar, :with => ["Michel", { :include => :friendships, :select => "name" }], :return => true)
205
+ end
206
+
207
+ should "succeeds if expectations are met" do
208
+ assert_nothing_raised do
209
+ @foo.bar "Michel", { :select => "name", :include => :friendships, :select => "name" }
210
+ end
211
+ end
212
+ end
213
+
214
+ context "side effects" do
215
+ setup do
216
+ @foo = Foo.new
217
+ expect(Bar, :foo, :with => [1, 2, 3], :return => "Bar/Bar")
218
+ end
219
+
220
+ should "don't affect the interface" do
221
+ assert_equal true, @foo.nom
222
+ end
223
+
224
+ should "detect the method call as a side effect" do
225
+ override(Bar, :foo => lambda { |*_| raise ArgumentError })
226
+ assert_raise ArgumentError do
227
+ @foo.nom
228
+ end
229
+ end
230
+
231
+ should "don't affect the interfaces!" do
232
+ @foo = Foo.new
233
+ expect(Bar, :foo, :with => [1, 2, 3], :return => "Bar/Bar")
234
+ assert_equal true, @foo.nom
235
+ override(Bar, :foo => lambda { |*_| raise ArgumentError })
236
+ assert_raise ArgumentError do
237
+ @foo.nom
238
+ end
239
+ end
240
+ end
241
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: override
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.8
5
+ platform: ruby
6
+ authors:
7
+ - Michel Martens
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-13 00:00:00 -02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: metaid
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "1.0"
24
+ version:
25
+ description:
26
+ email: michel@soveran.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/override.rb
35
+ - README.markdown
36
+ - LICENSE
37
+ - Rakefile
38
+ - test/all_test.rb
39
+ has_rdoc: false
40
+ homepage: http://github.com/soveran/override
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.1
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: The as-simple-as-possible-but-not-simpler stubbing library.
65
+ test_files: []
66
+