surrogate 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### 0.6.1
2
+
3
+ * bang methods map to ivars suffixed with `_b`, because you can't have a bang in an ivar name
4
+ * Add general syntax for overriding values (e.g. for use with operators) `will_overrides`
5
+ * block assertions can specify that exceptions should get raised (still shitty error messages, though) The interface mimicks RSpec's `#raise_error` matcher
6
+
1
7
  ### 0.6.0
2
8
 
3
9
  * Setting an override still requires the invoking code to call with the correct signature
data/Readme.md CHANGED
@@ -78,15 +78,16 @@ end
78
78
  MockClient.new.request 3 # => ["result1", "result2", "result3"]
79
79
  ```
80
80
 
81
- You don't need a **default if you set the ivar** of the same name (replace `?` with `_p` for predicates, since you can't have question marks in ivar names)
81
+ You don't need a **default if you set the ivar** of the same name (replace `?` with `_p` for predicates, and `!` with `_b` for bang methods, since you can't have question marks or bangs in ivar names)
82
82
  Note that methods without bodies will not have their arguments checked, and will not be asserted against when comparing signatures.
83
83
 
84
84
  ```ruby
85
85
  class MockClient
86
86
  Surrogate.endow self
87
- define(:initialize) { |id| @id, @connected_p = id, true }
87
+ define(:initialize) { |id| @id, @connected_p, @reconnect_b = id, true, true }
88
88
  define :id
89
89
  define :connected?
90
+ define :reconnect!
90
91
  end
91
92
  MockClient.new(12).id # => 12
92
93
  ```
@@ -458,6 +459,7 @@ Special Thanks
458
459
 
459
460
  * [Kyle Hargraves](https://github.com/pd) for changing the name of his internal gem so that I could take Surrogate
460
461
  * [David Chelimsky](http://blog.davidchelimsky.net/) for pairing with me to make Surrogate integrate better with RSpec
461
- * [Corey Haines](http://coreyhaines.com/) for pairing on substitutability with me
462
462
  * [Enova](http://www.enovafinancial.com/) for giving me time and motivation to work on this during Enova Labs.
463
463
  * [8th Light](http://8thlight.com/) for giving me time to work on this during our weekly Wazas, and the general encouragement and interest
464
+ * The people who have paired with me on this: [Corey Haines](http://coreyhaines.com/) on substitutability, [Kori Roys](https://github.com/koriroys) on `#last_instance`, [Wai Lee](https://github.com/skatenerd) on the new assertion syntax
465
+ * The people who have contributed: [Michael Baker](with m://github.com/michaelbaker) on the readme.
@@ -98,16 +98,17 @@ MockClient.new.request 3 # => ["result1", "result2", "result3"]
98
98
  <% end %>
99
99
  ```
100
100
 
101
- You don't need a **default if you set the ivar** of the same name (replace `?` with `_p` for predicates, since you can't have question marks in ivar names)
101
+ You don't need a **default if you set the ivar** of the same name (replace `?` with `_p` for predicates, and `!` with `_b` for bang methods, since you can't have question marks or bangs in ivar names)
102
102
  Note that methods without bodies will not have their arguments checked, and will not be asserted against when comparing signatures.
103
103
 
104
104
  ```ruby
105
105
  <% test 'overriding default by setting the ivar', with: :magic_comments do %>
106
106
  class MockClient
107
107
  Surrogate.endow self
108
- define(:initialize) { |id| @id, @connected_p = id, true }
108
+ define(:initialize) { |id| @id, @connected_p, @reconnect_b = id, true, true }
109
109
  define :id
110
110
  define :connected?
111
+ define :reconnect!
111
112
  end
112
113
  MockClient.new(12).id # => 12
113
114
  <% end %>
@@ -548,6 +549,7 @@ Special Thanks
548
549
 
549
550
  * [Kyle Hargraves](https://github.com/pd) for changing the name of his internal gem so that I could take Surrogate
550
551
  * [David Chelimsky](http://blog.davidchelimsky.net/) for pairing with me to make Surrogate integrate better with RSpec
551
- * [Corey Haines](http://coreyhaines.com/) for pairing on substitutability with me
552
552
  * [Enova](http://www.enovafinancial.com/) for giving me time and motivation to work on this during Enova Labs.
553
553
  * [8th Light](http://8thlight.com/) for giving me time to work on this during our weekly Wazas, and the general encouragement and interest
554
+ * The people who have paired with me on this: [Corey Haines](http://coreyhaines.com/) on substitutability, [Kori Roys](https://github.com/koriroys) on `#last_instance`, [Wai Lee](https://github.com/skatenerd) on the new assertion syntax
555
+ * The people who have contributed: [Michael Baker](with m://github.com/michaelbaker) on the readme.
@@ -32,10 +32,11 @@ class Surrogate
32
32
 
33
33
  def endow_klass
34
34
  klass.extend ClassMethods
35
- add_hatchery_to klass
36
- enable_defining_methods klass
35
+ add_hatchery_to klass
36
+ enable_defining_methods klass
37
37
  klass.send :include, InstanceMethods
38
- invoke_hooks klass
38
+ enable_generic_override klass
39
+ invoke_hooks klass
39
40
  end
40
41
 
41
42
  def endow_singleton_class
@@ -43,10 +44,17 @@ class Surrogate
43
44
  enable_defining_methods singleton
44
45
  singleton.module_eval &block if block
45
46
  klass.instance_variable_set :@hatchling, Hatchling.new(klass, hatchery)
46
- invoke_hooks singleton
47
+ invoke_hooks singleton
47
48
  klass
48
49
  end
49
50
 
51
+ def enable_generic_override(klass)
52
+ klass.__send__ :define_method, :will_override do |method_name, *args, &block|
53
+ @hatchling.prepare_method method_name, args, &block
54
+ self
55
+ end
56
+ end
57
+
50
58
  def invoke_hooks(klass)
51
59
  self.class.hooks.each { |hook| hook.call klass }
52
60
  end
@@ -25,6 +25,7 @@ class Surrogate
25
25
  end
26
26
 
27
27
  def prepare_method(method_name, args, &block)
28
+ must_know method_name
28
29
  set_ivar method_name, Value.factory(*args, &block)
29
30
  end
30
31
 
@@ -75,11 +76,35 @@ class Surrogate
75
76
  end
76
77
 
77
78
  def ivar_for(method_name)
78
- case method_name
79
- when /\?$/
80
- "@#{method_name.to_s.chop}_p"
81
- else
82
- "@#{method_name}"
79
+ method_name = method_name.to_s
80
+ if method_name.end_with? ?? then "@#{method_name.to_s.chop}_p"
81
+ elsif method_name.end_with? ?! then "@#{method_name.to_s.chop}_b"
82
+ elsif method_name == '[]' then '@_brackets'
83
+ elsif method_name == '**' then '@_splat_splat'
84
+ elsif method_name == '!@' then '@_ubang'
85
+ elsif method_name == '+@' then '@_uplus'
86
+ elsif method_name == '-@' then '@_uminus'
87
+ elsif method_name == ?* then '@_splat'
88
+ elsif method_name == ?/ then '@_divide'
89
+ elsif method_name == ?% then '@_percent'
90
+ elsif method_name == ?+ then '@_plus'
91
+ elsif method_name == ?- then '@_minus'
92
+ elsif method_name == '>>' then '@_shift_right'
93
+ elsif method_name == '<<' then '@_shift_left'
94
+ elsif method_name == ?& then '@_ampersand'
95
+ elsif method_name == ?^ then '@_caret'
96
+ elsif method_name == ?| then '@_bang'
97
+ elsif method_name == '<=' then '@_less_eq'
98
+ elsif method_name == ?< then '@_less'
99
+ elsif method_name == ?> then '@_greater'
100
+ elsif method_name == '>=' then '@_greater_eq'
101
+ elsif method_name == '<=>' then '@_spaceship'
102
+ elsif method_name == '==' then '@_2eq'
103
+ elsif method_name == '===' then '@_3eq'
104
+ elsif method_name == '!=' then '@_not_eq'
105
+ elsif method_name == '=~' then '@_eq_tilde'
106
+ elsif method_name == '!~' then '@_bang_tilde'
107
+ else "@#{method_name}"
83
108
  end
84
109
  end
85
110
 
@@ -0,0 +1,127 @@
1
+ class Surrogate
2
+ module RSpec
3
+ class WithFilter
4
+ class BlockAsserter
5
+ class RaiseAsserter
6
+ def initialize(arg, message)
7
+ @assertion = if arg.kind_of? String
8
+ match_message arg
9
+ elsif arg.kind_of? Regexp
10
+ match_regexp arg
11
+ elsif exception_and_message? arg, message
12
+ match_exception_and_message arg, message
13
+ else
14
+ raise ArgumentError, "raising(#{arg.inspect}, #{message.inspect}) are not valid arguments"
15
+ end
16
+ end
17
+
18
+ def call(exception)
19
+ @assertion.call exception
20
+ end
21
+
22
+ private
23
+
24
+ def exception_and_message?(exception_class, message)
25
+ return false unless exception_class.kind_of? Class
26
+ return false unless exception_class.ancestors.include? Exception
27
+ return false unless message.kind_of?(NilClass) || message.kind_of?(String) || message.kind_of?(Regexp)
28
+ true
29
+ end
30
+
31
+ def match_message(message)
32
+ -> exception { message == exception.message }
33
+ end
34
+
35
+ def match_regexp(regexp)
36
+ -> exception { regexp =~ exception.message }
37
+ end
38
+
39
+ def match_exception_and_message(exception_class, message)
40
+ -> exception do
41
+ return false unless exception.kind_of? exception_class
42
+ return message == exception.message if message.kind_of? String
43
+ return message =~ exception.message if message.kind_of? Regexp
44
+ true
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ class BlockAsserter
51
+ def initialize(definition_block)
52
+ @call_with = Invocation.new []
53
+ definition_block.call self
54
+ end
55
+
56
+ def call_with(*args, &block)
57
+ @call_with = Invocation.new args, &block
58
+ end
59
+
60
+ def returns(value=nil, &block)
61
+ @returns = block || lambda { |returned| returned.should == value }
62
+ end
63
+
64
+ def before(&block)
65
+ @before = block
66
+ end
67
+
68
+ def after(&block)
69
+ @after = block
70
+ end
71
+
72
+ # arg can be a string (expected message)
73
+ # a regex (matches the actual message)
74
+ # an exception (type of exception expected to be raised)
75
+ def raising(arg, message=nil)
76
+ @raising = RaiseAsserter.new arg, message
77
+ end
78
+
79
+ def arity(n)
80
+ @arity = n
81
+ end
82
+
83
+ def matches?(block_to_test)
84
+ matches = before_matches? block_to_test
85
+ matches &&= return_value_matches? block_to_test
86
+ matches &&= arity_matches? block_to_test
87
+ matches &&= after_matches? block_to_test
88
+ matches
89
+ end
90
+
91
+ private
92
+
93
+ # matches if no return specified, or returned value == specified value
94
+ # also matches the block
95
+ def return_value_matches?(block_to_test)
96
+ returned_value = block_to_test.call(*@call_with.args, &@call_with.block)
97
+ @returns.call returned_value if @returns
98
+ @raising.nil?
99
+ rescue ::RSpec::Expectations::ExpectationNotMetError
100
+ false
101
+ rescue Exception # !!!!!!WARNING!!!!!! (look here if things go wrong)
102
+ @raising && @raising.call($!)
103
+ end
104
+
105
+ # matches if the first time it is called, it raises nothing
106
+ def before_matches?(*)
107
+ @before_has_been_invoked || (@before && @before.call)
108
+ ensure
109
+ return @before_has_been_invoked = true unless $!
110
+ end
111
+
112
+ # matches if nothing is raised
113
+ def after_matches?(block_to_test)
114
+ @after && @after.call
115
+ true
116
+ end
117
+
118
+ def arity_matches?(block_to_test)
119
+ return true unless @arity
120
+ block_to_test.arity == @arity
121
+ end
122
+
123
+ attr_accessor :block_to_test
124
+ end
125
+ end
126
+ end
127
+ end
@@ -1,75 +1,9 @@
1
+ require 'surrogate/rspec/block_asserter'
2
+
1
3
  class Surrogate
2
4
  module RSpec
3
5
  class WithFilter
4
6
 
5
- class BlockAsserter
6
- def initialize(definition_block)
7
- @call_with = Invocation.new []
8
- definition_block.call self
9
- end
10
-
11
- def call_with(*args, &block)
12
- @call_with = Invocation.new args, &block
13
- end
14
-
15
- def returns(value=nil, &block)
16
- @returns = block || lambda { |returned| returned.should == value }
17
- end
18
-
19
- def before(&block)
20
- @before = block
21
- end
22
-
23
- def after(&block)
24
- @after = block
25
- end
26
-
27
- def arity(n)
28
- @arity = n
29
- end
30
-
31
- def matches?(block_to_test)
32
- matches = before_matches? block_to_test
33
- matches &&= return_value_matches? block_to_test
34
- matches &&= arity_matches? block_to_test
35
- matches &&= after_matches? block_to_test
36
- matches
37
- end
38
-
39
- private
40
-
41
- # matches if no return specified, or returned value == specified value
42
- def return_value_matches?(block_to_test)
43
- returned_value = block_to_test.call(*@call_with.args, &@call_with.block)
44
- @returns.call returned_value if @returns
45
- true
46
- rescue ::RSpec::Expectations::ExpectationNotMetError
47
- false
48
- end
49
-
50
- # matches if the first time it is called, it raises nothing
51
- def before_matches?(*)
52
- @before_has_been_invoked || (@before && @before.call)
53
- ensure
54
- return @before_has_been_invoked = true unless $!
55
- end
56
-
57
- # matches if nothing is raised
58
- def after_matches?(block_to_test)
59
- @after && @after.call
60
- true
61
- end
62
-
63
- def arity_matches?(block_to_test)
64
- return true unless @arity
65
- block_to_test.arity == @arity
66
- end
67
-
68
- attr_accessor :block_to_test
69
- end
70
-
71
-
72
-
73
7
  class RSpecMatchAsserter
74
8
  attr_accessor :actual_invocation, :expected_invocation
75
9
 
@@ -1,3 +1,3 @@
1
1
  class Surrogate
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.1"
3
3
  end
@@ -65,24 +65,40 @@ describe 'define' do
65
65
  end
66
66
 
67
67
  it 'returns the object' do
68
- instance = mocked_class.new
69
68
  instance.will_wink(:quickly).should equal instance
70
69
  end
71
70
  end
72
71
 
73
72
  describe 'will_<api_method> with multiple arguments' do
74
73
  it 'returns the object' do
75
- instance = mocked_class.new
76
74
  instance.will_wink(1, 2, 3).should equal instance
77
75
  end
78
76
 
79
77
  # Is there something useful the error could say?
80
78
  it 'creates a queue of things to find and raises a QueueEmpty error if there are none left' do
81
- mock = mocked_class.new
82
- mock.will_wink :quickly, [:slowly]
83
- mock.wink.should == :quickly
84
- mock.wink.should == [:slowly]
85
- expect { mock.wink }.to raise_error Surrogate::Value::ValueQueue::QueueEmpty
79
+ instance.will_wink :quickly, [:slowly]
80
+ instance.wink.should == :quickly
81
+ instance.wink.should == [:slowly]
82
+ expect { instance.wink }.to raise_error Surrogate::Value::ValueQueue::QueueEmpty
83
+ end
84
+ end
85
+
86
+ describe 'will_override(method_name, ...)' do
87
+ it 'returns the object' do
88
+ instance.will_override(:wink, :quickly).should equal instance
89
+ end
90
+
91
+ it 'is a dynamic way to override a value (useful for operators)' do
92
+ instance.will_override :wink, :quickly
93
+ instance.wink.should == :quickly
94
+ instance.will_override :wink, :quickly, [:slowly]
95
+ instance.wink.should == :quickly
96
+ instance.wink.should == [:slowly]
97
+ end
98
+
99
+ it 'raises an error if you try to override a nonexistent method' do
100
+ expect { instance.will_override :whateva, 123 }
101
+ .to raise_error Surrogate::UnknownMethod, %(doesn't know "whateva", only knows "wink")
86
102
  end
87
103
  end
88
104
 
@@ -90,18 +106,18 @@ describe 'define' do
90
106
  it 'raises the error on method invocation' do
91
107
  mocked_class = Surrogate.endow(Class.new)
92
108
  mocked_class.define :connect
93
- mock = mocked_class.new
109
+ instance = mocked_class.new
94
110
  error = StandardError.new("some message")
95
111
 
96
112
  # for single invocation
97
- mock.will_connect error
98
- expect { mock.connect }.to raise_error StandardError, "some message"
113
+ instance.will_connect error
114
+ expect { instance.connect }.to raise_error StandardError, "some message"
99
115
 
100
116
  # for queue
101
- mock.will_connect 1, error, 2
102
- mock.connect.should == 1
103
- expect { mock.connect }.to raise_error StandardError, "some message"
104
- mock.connect.should == 2
117
+ instance.will_connect 1, error, 2
118
+ instance.connect.should == 1
119
+ expect { instance.connect }.to raise_error StandardError, "some message"
120
+ instance.connect.should == 2
105
121
  end
106
122
  end
107
123
  end
@@ -122,24 +138,21 @@ describe 'define' do
122
138
  end
123
139
 
124
140
  it 'returns the object' do
125
- instance = mocked_class.new
126
141
  instance.will_have_age(123).should equal instance
127
142
  end
128
143
  end
129
144
 
130
145
  describe 'wil_have_<api_method> with multiple arguments' do
131
146
  it 'returns the object' do
132
- instance = mocked_class.new
133
147
  instance.will_have_age(1,2,3).should equal instance
134
148
  end
135
149
 
136
150
  # Is there something useful the error could say?
137
151
  it 'creates a queue of things to find and raises a QueueEmpty error if there are none left' do
138
- mock = mocked_class.new
139
- mock.will_have_age 12, 34
140
- mock.age.should == 12
141
- mock.age.should == 34
142
- expect { mock.age }.to raise_error Surrogate::Value::ValueQueue::QueueEmpty
152
+ instance.will_have_age 12, 34
153
+ instance.age.should == 12
154
+ instance.age.should == 34
155
+ expect { instance.age }.to raise_error Surrogate::Value::ValueQueue::QueueEmpty
143
156
  end
144
157
  end
145
158
  end
@@ -157,35 +170,41 @@ describe 'define' do
157
170
 
158
171
  it 'raises an UnpreparedMethodError when it has no default block' do
159
172
  mocked_class.define :meth
160
- expect { mocked_class.new.meth }.to raise_error(Surrogate::UnpreparedMethodError, /meth/)
173
+ expect { instance.meth }.to raise_error Surrogate::UnpreparedMethodError, /meth/
161
174
  end
162
175
 
163
176
  it 'considers ivars of the same name to be its default when it has no suffix' do
164
177
  mocked_class.define :meth
165
- mocked = mocked_class.new
166
- mocked.instance_variable_set :@meth, 123
167
- mocked.meth.should == 123
178
+ instance.instance_variable_set :@meth, 123
179
+ instance.meth.should == 123
168
180
  end
169
181
 
170
182
  it 'considers ivars ending in _p to be its default when it ends in a question mark' do
171
183
  mocked_class.define :meth?
172
- mocked = mocked_class.new
173
- mocked.instance_variable_set :@meth_p, 123
174
- mocked.meth?.should == 123
184
+ instance.instance_variable_set :@meth_p, 123
185
+ instance.meth?.should == 123
186
+ instance.will_have_meth? 456
187
+ instance.meth?.should == 456
188
+ end
189
+
190
+ it 'considers ivars ending in _b to be its default when it ends in a bang' do
191
+ mocked_class.define :meth!
192
+ instance.instance_variable_set :@meth_b, 123
193
+ instance.meth!.should == 123
194
+ instance.will_have_meth! 456
195
+ instance.meth!.should == 456
175
196
  end
176
197
 
177
198
  it 'reverts to the default block if invoked and having no ivar' do
178
199
  mocked_class.define(:meth) { 123 }
179
- mocked = mocked_class.new
180
- mocked.instance_variable_get(:@meth).should be_nil
181
- mocked.meth.should == 123
200
+ instance.instance_variable_get(:@meth).should be_nil
201
+ instance.meth.should == 123
182
202
  end
183
203
 
184
204
  it 'raises arity errors, even if the value is overridden' do
185
205
  mocked_class.define(:meth) { }
186
- mocked = mocked_class.new
187
- mocked.instance_variable_set :@meth, "abc"
188
- expect { mocked.meth "extra", "args" }.to raise_error ArgumentError, /wrong number of arguments \(2 for 0\)/
206
+ instance.instance_variable_set :@meth, "abc"
207
+ expect { instance.meth "extra", "args" }.to raise_error ArgumentError, /wrong number of arguments \(2 for 0\)/
189
208
  end
190
209
 
191
210
  it 'does not raise arity errors, when there is no default block and the value is overridden' do
@@ -204,13 +223,11 @@ describe 'define' do
204
223
  describe 'it takes a block whos return value will be used as the default' do
205
224
  specify 'the block is instance evaled' do
206
225
  mocked_class.define(:meth) { self }
207
- instance = mocked_class.new
208
226
  instance.meth.should equal instance
209
227
  end
210
228
 
211
229
  specify 'arguments passed to the method will be passed to the block' do
212
230
  mocked_class.define(:meth) { |*args| args }
213
- instance = mocked_class.new
214
231
  instance.meth(1).should == [1]
215
232
  instance.meth(1, 2).should == [1, 2]
216
233
  end
@@ -231,9 +248,8 @@ describe 'define' do
231
248
  it 'raises an error if asked about invocations for api methods it does not know' do
232
249
  mocked_class.define :meth1
233
250
  mocked_class.define :meth2
234
- mock = mocked_class.new
235
- expect { invocations mock, :meth1 }.to_not raise_error
236
- expect { invocations mock, :meth3 }.to raise_error Surrogate::UnknownMethod, /doesn't know "meth3", only knows "meth1", "meth2"/
251
+ expect { invocations instance, :meth1 }.to_not raise_error
252
+ expect { invocations instance, :meth3 }.to raise_error Surrogate::UnknownMethod, /doesn't know "meth3", only knows "meth1", "meth2"/
237
253
  end
238
254
  end
239
255
 
@@ -3,6 +3,10 @@ require 'spec_helper'
3
3
  # these all need error messages
4
4
  describe 'RSpec matchers', 'have_been_told_to(...).with { |block| }' do
5
5
 
6
+ def fails(&block)
7
+ expect { block.call }.to raise_error RSpec::Expectations::ExpectationNotMetError
8
+ end
9
+
6
10
  let(:dir) { Surrogate.endow(Class.new) { define(:chdir) { |dir_path| nil }}}
7
11
  let(:dir_path) { '/some/dir/path' }
8
12
 
@@ -21,7 +25,7 @@ describe 'RSpec matchers', 'have_been_told_to(...).with { |block| }' do
21
25
  dir.should have_been_told_to(:chdir).with(dir_path) { }
22
26
  end
23
27
 
24
- it "fails if the arguments don't match, even if the block does" do
28
+ it "fails if the block's arguments don't match" do
25
29
  dir.chdir(dir_path) { }
26
30
  dir.should_not have_been_told_to(:chdir).with(dir_path.reverse) { }
27
31
  dir.should have_been_told_to(:chdir).with(dir_path) { }
@@ -61,6 +65,7 @@ describe 'RSpec matchers', 'have_been_told_to(...).with { |block| }' do
61
65
  let(:file_name) { 'some_file_name.ext' }
62
66
  let(:file_body) { 'some file body' }
63
67
 
68
+ # reword this a bit
64
69
  describe 'the .before and .after hooks' do
65
70
  specify "take blocks which it will evaluate before/after invoking the submitted_block" do
66
71
  dir.chdir(dir_path) { file.write file_name, file_body }
@@ -70,7 +75,7 @@ describe 'RSpec matchers', 'have_been_told_to(...).with { |block| }' do
70
75
  }
71
76
  end
72
77
 
73
- example "multiple invocations wrong number of times" do
78
+ example "example: multiple invocations wrong number of times" do
74
79
  dir.chdir(dir_path) { file.write file_name, file_body }
75
80
  dir.chdir(dir_path) { file.write file_name, file_body }
76
81
  dir.should_not have_been_told_to(:chdir).times(1).with(dir_path) { |block|
@@ -79,7 +84,7 @@ describe 'RSpec matchers', 'have_been_told_to(...).with { |block| }' do
79
84
  }
80
85
  end
81
86
 
82
- example "multiple invocations correct number of times" do
87
+ example "example: multiple invocations correct number of times" do
83
88
  dir.chdir(dir_path) { file.write file_name, file_body }
84
89
  dir.chdir(dir_path) { file.write file_name, file_body }
85
90
  dir.should have_been_told_to(:chdir).times(2).with(dir_path) { |block|
@@ -97,9 +102,7 @@ describe 'RSpec matchers', 'have_been_told_to(...).with { |block| }' do
97
102
  klass.new.meth { |a,b| }.should have_been_told_to(:meth).with { |b| b.arity 2 }
98
103
  klass.new.meth { |a,b,c| }.should have_been_told_to(:meth).with { |b| b.arity 3 }
99
104
  klass.new.meth { |*a| }.should have_been_told_to(:meth).with { |b| b.arity -1 }
100
- expect {
101
- klass.new.meth { |a| }.should have_been_told_to(:meth).with { |b| b.arity 123 }
102
- }.to raise_error RSpec::Expectations::ExpectationNotMetError
105
+ fails { klass.new.meth { |a| }.should have_been_told_to(:meth).with { |b| b.arity 123 } }
103
106
  end
104
107
  end
105
108
 
@@ -124,6 +127,53 @@ describe 'RSpec matchers', 'have_been_told_to(...).with { |block| }' do
124
127
  end
125
128
 
126
129
  describe ".raising is like RSpec's raise_error interface" do
127
- it { pending 'IDK what I want this to be like yet' }
130
+ let(:klass) { Surrogate.endow(Class.new).define(:meth) { |&block| self } }
131
+
132
+ it 'fails when an exception is expected but not raised' do
133
+ fails { klass.new.meth { }.was told_to(:meth).with { |b| b.raising "not whatever" } }
134
+ end
135
+
136
+ it 'fails when an exception is raised but not expected' do
137
+ fails { klass.new.meth { raise }.was told_to(:meth).with { |b| } }
138
+ end
139
+
140
+ # expect { raise "hello world" }.to raise_error "hello world"
141
+ it 'can take a string which must match the message' do
142
+ klass.new.meth { raise "whatever" }.was told_to(:meth).with { |b| b.raising "whatever" }
143
+ fails { klass.new.meth { raise "whatever" }.was told_to(:meth).with { |b| b.raising "not whatever" } }
144
+ end
145
+
146
+ # expect { raise "hello world" }.to raise_error /hello/
147
+ it 'can take a regex which must match the message' do
148
+ klass.new.meth { raise "whatever" }.was told_to(:meth).with { |b| b.raising /whatev/ }
149
+ fails { klass.new.meth { raise "whatever" }.was told_to(:meth).with { |b| b.raising /not a match/ } }
150
+ end
151
+
152
+ # expect { raise ArgumentError }.to raise_error ArgumentError
153
+ it 'can take an exception class which must be raised' do
154
+ klass.new.meth { raise ArgumentError, 'some message' }.was told_to(:meth).with { |b| b.raising ArgumentError }
155
+ fails { klass.new.meth { raise ArgumentError, 'some message' }.was told_to(:meth).with { |b| b.raising RuntimeError } }
156
+ end
157
+
158
+ # expect { raise ArgumentError, "whatever" }.to raise_error ArgumentError, "whatever"
159
+ it 'can take an exception class and string' do
160
+ klass.new.meth { raise ArgumentError, 'whatever' }.was told_to(:meth).with { |b| b.raising ArgumentError, 'whatever' }
161
+ fails { klass.new.meth { raise RuntimeError, 'whatever' }.was told_to(:meth).with { |b| b.raising ArgumentError, 'whatever' } }
162
+ fails { klass.new.meth { raise ArgumentError, 'whatever' }.was told_to(:meth).with { |b| b.raising ArgumentError, 'not whatever' } }
163
+ end
164
+
165
+ # expect { raise ArgumentError, 'abc' }.to raise_error ArgumentError, /abc/
166
+ it 'can take an exception class and regex' do
167
+ klass.new.meth { raise ArgumentError, "whatever" }.was told_to(:meth).with { |b| b.raising ArgumentError, /what/ }
168
+ fails { klass.new.meth { raise RuntimeError, "whatever" }.was told_to(:meth).with { |b| b.raising ArgumentError, /whatever/ } }
169
+ fails { klass.new.meth { raise ArgumentError, "whatever" }.was told_to(:meth).with { |b| b.raising ArgumentError, /not a match/ } }
170
+ end
171
+
172
+ it 'raises an error when given anything else' do
173
+ bad_args = -> &block { expect { block.call }.to raise_error ArgumentError, /not valid arguments/ }
174
+ bad_args.call { klass.new.meth {}.was told_to(:meth).with { |b| b.raising Class } } # not an exception
175
+ bad_args.call { klass.new.meth {}.was told_to(:meth).with { |b| b.raising [] } } # not a string/regex
176
+ bad_args.call { klass.new.meth {}.was told_to(:meth).with { |b| b.raising ArgumentError, [] } } # 2nd param is not a string/regex
177
+ end
128
178
  end
129
179
  end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'mapping method names to ivars' do
4
+ let(:mocked_class) { Surrogate.endow Class.new }
5
+
6
+ [ :[] , '@_brackets',
7
+ :** , '@_splat_splat',
8
+ :'!@' , '@_ubang',
9
+ :'+@' , '@_uplus',
10
+ :'-@' , '@_uminus',
11
+ :* , '@_splat',
12
+ :/ , '@_divide',
13
+ :% , '@_percent',
14
+ :+ , '@_plus',
15
+ :- , '@_minus',
16
+ :>> , '@_shift_right',
17
+ :<< , '@_shift_left',
18
+ :& , '@_ampersand',
19
+ :^ , '@_caret',
20
+ :| , '@_bang',
21
+ :<= , '@_less_eq',
22
+ :< , '@_less',
23
+ :> , '@_greater',
24
+ :>= , '@_greater_eq',
25
+ :<=> , '@_spaceship',
26
+ :== , '@_2eq',
27
+ :=== , '@_3eq',
28
+ :!= , '@_not_eq',
29
+ :=~ , '@_eq_tilde',
30
+ :!~ , '@_bang_tilde',
31
+ ].each_slice 2 do |method_name, ivar|
32
+ it "maps #{method_name} to #{ivar}" do
33
+ mocked_class.define method_name
34
+ instance = mocked_class.new
35
+ instance.instance_variable_set ivar, 123
36
+ instance.send method_name
37
+ end
38
+ end
39
+ end
data/todo CHANGED
@@ -2,16 +2,12 @@ Urgent (things I want to do immediately, formatted as the git commits I will use
2
2
  ---------------------------------------------------------------------------------
3
3
 
4
4
  * be smart enough to handle method missing
5
- * still check method args, even when value is overridden
6
5
  * tests around the error messages of types
7
6
  * Substitutability can check argument names
8
7
  * substitute_for should not depend on rspec-expectations
9
8
  * Error messages on blocks are actually useful
10
- * Defined methods params must match arguments (BREAKING CHANGE)
11
- * Update the readme (new syntax, and move the script into the changelog)
12
9
  * error message
13
10
  "Doesn't know initialize, only knows " <-- fix that shit
14
- * blocks assertions can specify raising
15
11
 
16
12
  TODO (next up after urgent, these will happen whenever I get around to it)
17
13
  --------------------------------------------------------------------------
@@ -20,13 +16,10 @@ TODO (next up after urgent, these will happen whenever I get around to it)
20
16
  * Remove dependency on all of RSpec and only depend on rspec-core, then have AC tests for the other shit
21
17
  * Add a better explanation for motivations
22
18
  * Figure out whether I'm supposed to be using clone or dup for the object -.^ (looks like there may also be an `initialize_copy` method I can take advantage of instead of crazy stupid shit I'm doing now)
23
- * don't blow up when delegating to the Object#initialize with args (do I still want this, or do I want to force arity matching (and maybe even variable name matching)?)
24
19
  * config: rspec_mocks loaded, whether unprepared blocks should raise or just return nil
25
20
  * extract surrogate/rspec into its own gem
26
- * support subset-substitutabilty not being able to touch real methods (e.g. #respond_to?)
27
21
  * make substitutability matcher go either way
28
22
  * make substitutability matcher not care whether either are surrogates
29
- * Add support for operators
30
23
 
31
24
 
32
25
  Future Features (Things that probably should eventually happen, but not anytime soon unless I get really inspired to work on this shit)
@@ -34,8 +27,7 @@ Future Features (Things that probably should eventually happen, but not anytime
34
27
 
35
28
  * Can endow a class multiple times, results aggregate instead of override
36
29
  * figure out how to talk about callbacks like #on_success
37
- * have some sort of reinitialization that can hook into setup/teardown steps of test suite
38
- * Support arity checking as part of substitutability
30
+ * have some sort of reinitialization that can hook into setup/teardown steps of test suite (maybe, or maybe I'm happy with what I have)
39
31
  * Ability to disassociate the method name from the test (e.g. you shouldn't need to change a test just because you change a name)
40
32
  * ability to declare normal methods as being part of the API
41
33
  * ability to declare a define that uses the overridden method as the body, but can still act like an api method
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: surrogate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-23 00:00:00.000000000 Z
12
+ date: 2012-10-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70261539914140 !ruby/object:Gem::Requirement
16
+ requirement: &70147459182300 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70261539914140
24
+ version_requirements: *70147459182300
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70261539929980 !ruby/object:Gem::Requirement
27
+ requirement: &70147459181540 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '2.4'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70261539929980
35
+ version_requirements: *70147459181540
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: mountain_berry_fields
38
- requirement: &70261539929360 !ruby/object:Gem::Requirement
38
+ requirement: &70147459180560 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.0.3
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70261539929360
46
+ version_requirements: *70147459180560
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mountain_berry_fields-rspec
49
- requirement: &70261539928860 !ruby/object:Gem::Requirement
49
+ requirement: &70147459179620 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.0.3
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70261539928860
57
+ version_requirements: *70147459179620
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: mountain_berry_fields-magic_comments
60
- requirement: &70261539928300 !ruby/object:Gem::Requirement
60
+ requirement: &70147459178820 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: 1.0.1
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70261539928300
68
+ version_requirements: *70147459178820
69
69
  description: Framework to aid in handrolling mock/spy objects.
70
70
  email:
71
71
  - josh.cheek@gmail.com
@@ -94,6 +94,7 @@ files:
94
94
  - lib/surrogate/porc_reflector.rb
95
95
  - lib/surrogate/rspec.rb
96
96
  - lib/surrogate/rspec/abstract_failure_message.rb
97
+ - lib/surrogate/rspec/block_asserter.rb
97
98
  - lib/surrogate/rspec/initialization_matcher.rb
98
99
  - lib/surrogate/rspec/invocation_matcher.rb
99
100
  - lib/surrogate/rspec/noun_matcher.rb
@@ -120,6 +121,7 @@ files:
120
121
  - spec/spec_helper.rb
121
122
  - spec/unit/api_comparer_spec.rb
122
123
  - spec/unit/argument_errorizer_spec.rb
124
+ - spec/unit/map_method_name_to_ivar_spec.rb
123
125
  - surrogate.gemspec
124
126
  - todo
125
127
  homepage: https://github.com/JoshCheek/surrogate
@@ -161,4 +163,5 @@ test_files:
161
163
  - spec/spec_helper.rb
162
164
  - spec/unit/api_comparer_spec.rb
163
165
  - spec/unit/argument_errorizer_spec.rb
166
+ - spec/unit/map_method_name_to_ivar_spec.rb
164
167
  has_rdoc: