override 0.0.8

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