mocoso 0.0.2 → 0.1.0
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.
- checksums.yaml +4 -4
- data/README.md +9 -5
- data/lib/mocoso.rb +46 -108
- data/mocoso.gemspec +1 -1
- data/test/scary_mocks_and_nice_stubs.rb +32 -70
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5c21876105f80fb7e2c5f0f72b09406e83be714
|
4
|
+
data.tar.gz: af2c896fb182aa84454381aaa4060b3a04245078
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5e18f8f38ba523988872a90be55bd2a3d0685258741fb13e238657c612c48c7fedbb1a2e9fa032f100b1ecb1d0385c817d62cfa7ad4b5b3b83c0ebf8960a402
|
7
|
+
data.tar.gz: 5f717db7f95cf129560e2e2c2f40b710d88860593e240d9d9afba63ee1fe99b5ac90ee8138f2edf3907933b2b7a5352c1332be6875527537c48227b9c908132f
|
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Motivation
|
|
12
12
|
Yes, there are a lot of good libraries out there, but I wanted one that
|
13
13
|
meets the following criteria:
|
14
14
|
|
15
|
-
*
|
15
|
+
* Always restore stubbed methods to their original implementations.
|
16
16
|
* Doesn't allow to stub or mock undefined methods.
|
17
17
|
* Doesn't monkey-patch any class or object.
|
18
18
|
* Test-framework agnostic (Doesn't need integration code).
|
@@ -36,14 +36,18 @@ A quick example (uses [Cutest][cutest]):
|
|
36
36
|
|
37
37
|
test 'mocking a class method' do
|
38
38
|
user = User.new
|
39
|
-
|
40
|
-
|
39
|
+
|
40
|
+
expect User, :find, with: [1], return: user do
|
41
|
+
assert_equal user, User.find(1)
|
42
|
+
end
|
41
43
|
end
|
42
44
|
|
43
45
|
test 'stubbing an instance method' do
|
44
46
|
user = User.new
|
45
|
-
|
46
|
-
|
47
|
+
|
48
|
+
stub user, valid?: true do
|
49
|
+
assert user.valid?
|
50
|
+
end
|
47
51
|
end
|
48
52
|
|
49
53
|
Check [Official Documentation][docs] for more details.
|
data/lib/mocoso.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Yet Another Simple Stub & Mock library, that also:
|
2
2
|
#
|
3
|
-
# -
|
3
|
+
# - Always restore stubbed methods to their original implementations.
|
4
4
|
# - Doesn't allow to stub or mock undefined methods.
|
5
5
|
# - Doesn't monkey-patch any class or object.
|
6
6
|
# - Test-framework agnostic (Doesn't need integration code).
|
@@ -22,14 +22,18 @@
|
|
22
22
|
#
|
23
23
|
# test 'mocking a class method' do
|
24
24
|
# user = User.new
|
25
|
-
#
|
26
|
-
#
|
25
|
+
#
|
26
|
+
# expect User, :find, with: [1], return: user do
|
27
|
+
# assert_equal user, User.find(1)
|
28
|
+
# end
|
27
29
|
# end
|
28
30
|
#
|
29
31
|
# test 'stubbing an instance method' do
|
30
32
|
# user = User.new
|
31
|
-
#
|
32
|
-
#
|
33
|
+
#
|
34
|
+
# stub user, valid?: true do
|
35
|
+
# assert user.valid?
|
36
|
+
# end
|
33
37
|
# end
|
34
38
|
#
|
35
39
|
# Note: this example uses the test framework Cutest[1]:
|
@@ -44,33 +48,35 @@
|
|
44
48
|
module Mocoso
|
45
49
|
# Raised by #expect when a expectation is not fulfilled.
|
46
50
|
#
|
47
|
-
# Mocoso.expect object, :method, with: 'argument', returns: nil
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
+
# Mocoso.expect object, :method, with: 'argument', returns: nil do
|
52
|
+
# object.method 'unexpected argument'
|
53
|
+
# # => Mocoso::ExpectationError: Expected ["argument"], got ["unexpected argument"]
|
54
|
+
# end
|
51
55
|
#
|
52
56
|
ExpectationError = Class.new StandardError
|
53
57
|
|
54
|
-
# Rewrites each method from
|
58
|
+
# Rewrites each method from +methods+ and defined in +object+. +methods+ is a
|
55
59
|
# Hash that represents stubbed method name symbols as keys and corresponding
|
56
|
-
# return values as values.
|
60
|
+
# return values as values. The methods are restored after the given +block+
|
61
|
+
# is executed.
|
57
62
|
#
|
58
63
|
# signup = SignupForm.new params[:user]
|
59
64
|
#
|
60
65
|
# signup.valid? # => false
|
61
|
-
# signup.save # => false
|
62
|
-
#
|
63
|
-
# Mocoso.stub signup, valid?: true, signup: true
|
64
66
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
+
# Mocoso.stub signup, :valid?, true do
|
68
|
+
# signup.valid? # => true
|
69
|
+
# end
|
67
70
|
#
|
68
71
|
# You can pass a callable object (responds to +call+) as a value:
|
69
72
|
#
|
70
|
-
# Mocoso.stub subject, foo: -> { "foo" }
|
73
|
+
# Mocoso.stub subject, foo: -> { "foo" } do
|
74
|
+
# subject.foo # => "foo"
|
75
|
+
# end
|
71
76
|
#
|
72
|
-
#
|
73
|
-
#
|
77
|
+
# Mocoso.stub subject, :bar, ->(value) { value } do
|
78
|
+
# subject.bar('foo') # => "foo"
|
79
|
+
# end
|
74
80
|
#
|
75
81
|
# If you try to stub a method that is not defined by the +object+,
|
76
82
|
# it raises an error.
|
@@ -78,79 +84,32 @@ module Mocoso
|
|
78
84
|
# Mocoso.stub Object.new, undefined: nil
|
79
85
|
# # => NameError: undefined method `undefined' for class `Object'
|
80
86
|
#
|
81
|
-
|
82
|
-
# method without side effects, you should pass a block.
|
83
|
-
#
|
84
|
-
# User.all.length
|
85
|
-
# # => 5
|
86
|
-
#
|
87
|
-
# Mocoso.stub User, all: [] do
|
88
|
-
# User.all.length
|
89
|
-
# # => 0
|
90
|
-
# end
|
91
|
-
#
|
92
|
-
# User.all.length
|
93
|
-
# # => 5
|
94
|
-
#
|
95
|
-
def stub object, methods
|
87
|
+
def stub object, method, result, &block
|
96
88
|
metaclass = object.singleton_class
|
89
|
+
stubbed_method = "__mocoso_#{method}"
|
97
90
|
|
98
|
-
|
99
|
-
metaclass.send :alias_method, stub_method_name(method), method
|
91
|
+
metaclass.send :alias_method, stubbed_method, method
|
100
92
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
if block_given?
|
109
|
-
begin
|
110
|
-
yield
|
111
|
-
ensure
|
112
|
-
unstub object, methods.keys
|
113
|
-
end
|
93
|
+
if result.respond_to?(:call)
|
94
|
+
metaclass.send(:define_method, method) { |*args| result.(*args) }
|
95
|
+
else
|
96
|
+
metaclass.send(:define_method, method) { result }
|
114
97
|
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Removes the specified stubbed +methods+ (added by calls to #stub or #expect) and
|
118
|
-
# restores the original behaviour of the methods before they were stubbed.
|
119
|
-
#
|
120
|
-
# object.foo # => "foo"
|
121
|
-
#
|
122
|
-
# Mocoso.stub object, foo: 'new foo'
|
123
|
-
# object.foo # => "new foo"
|
124
|
-
#
|
125
|
-
# Mocoso.unstub object, [:foo]
|
126
|
-
# object.foo #=> "foo"
|
127
|
-
#
|
128
|
-
# I personally use a block on #stub or #expect to avoid side effects, because if
|
129
|
-
# you #unstub a method which still has unsatisfied expectations, you may be
|
130
|
-
# removing the only way those expectations can be satisfied. Use it on your
|
131
|
-
# own responsibility.
|
132
|
-
#
|
133
|
-
# This method was born as a helper for #stub.
|
134
|
-
#
|
135
|
-
def unstub object, methods
|
136
|
-
metaclass = object.singleton_class
|
137
98
|
|
138
|
-
|
99
|
+
begin
|
100
|
+
yield
|
101
|
+
ensure
|
139
102
|
metaclass.send :undef_method, method
|
140
|
-
metaclass.send :alias_method, method,
|
141
|
-
metaclass.send :undef_method,
|
103
|
+
metaclass.send :alias_method, method, stubbed_method
|
104
|
+
metaclass.send :undef_method, stubbed_method
|
142
105
|
end
|
143
106
|
end
|
144
107
|
|
145
|
-
def stub_method_name name
|
146
|
-
"__mocoso_#{name}"
|
147
|
-
end
|
148
|
-
private :stub_method_name
|
149
|
-
|
150
108
|
# Expect that method +method+ is called with the arguments specified in the
|
151
109
|
# +:with+ option (defaults to +[]+ if it's not given) and returns the value
|
152
|
-
# specified in the +:
|
153
|
-
# Mocoso::ExpectationError error.
|
110
|
+
# specified in the +:returns+ option. If expectations are not met, it raises
|
111
|
+
# Mocoso::ExpectationError error. It uses #stub internally, so it will restore
|
112
|
+
# the methods to their original implementation after the +block+ is executed.
|
154
113
|
#
|
155
114
|
# class User < Model
|
156
115
|
# end
|
@@ -158,40 +117,19 @@ module Mocoso
|
|
158
117
|
# user = User[1]
|
159
118
|
#
|
160
119
|
# Mocoso.expect user, :update, with: [{ name: 'new name' }], returns: true
|
120
|
+
# subject.update unexpected: nil
|
121
|
+
# # => Mocoso::ExpectationError: Expected [{:name=>"new name"}], got [{:unexpected=>nil}]
|
161
122
|
#
|
162
|
-
#
|
163
|
-
# # => Mocoso::ExpectationError: Expected [{:name=>"new name"}], got [{:unexpected=>nil}]
|
164
|
-
#
|
165
|
-
# user.update name: 'new name'
|
166
|
-
# # => true
|
167
|
-
#
|
168
|
-
# Note that it will rewrite the method in +object+. If you want to set an
|
169
|
-
# expectation without side effects, you should pass a block.
|
170
|
-
#
|
171
|
-
# User.exists? 1
|
172
|
-
# # => false
|
173
|
-
#
|
174
|
-
# Mocoso.expect User, :exists?, with: [1], returns: true do
|
175
|
-
# User.exists? 1
|
123
|
+
# user.update name: 'new name'
|
176
124
|
# # => true
|
177
125
|
# end
|
178
126
|
#
|
179
|
-
|
180
|
-
# # => false
|
181
|
-
#
|
182
|
-
def expect object, method, options
|
127
|
+
def expect object, method, with: [], returns:, &block
|
183
128
|
expectation = -> *params {
|
184
|
-
with = options.fetch(:with) { [] }
|
185
|
-
|
186
129
|
raise ExpectationError, "Expected #{with}, got #{params}" if params != with
|
187
|
-
|
188
|
-
options[:return]
|
130
|
+
returns
|
189
131
|
}
|
190
132
|
|
191
|
-
|
192
|
-
stub object, method => expectation, &proc
|
193
|
-
else
|
194
|
-
stub object, method => expectation
|
195
|
-
end
|
133
|
+
stub object, method, expectation, &block
|
196
134
|
end
|
197
135
|
end
|
data/mocoso.gemspec
CHANGED
@@ -17,104 +17,66 @@ setup do
|
|
17
17
|
Subject.new
|
18
18
|
end
|
19
19
|
|
20
|
-
test 'raises error if
|
21
|
-
assert_raise { stub(subject,
|
20
|
+
test 'raises error if block is not given' do |subject|
|
21
|
+
assert_raise { stub(subject, foo: 'foo') }
|
22
22
|
end
|
23
23
|
|
24
|
-
test '
|
25
|
-
|
26
|
-
before_bar = subject.bar
|
27
|
-
|
28
|
-
stub subject, foo: 'new foo', bar: 'new bar'
|
29
|
-
|
30
|
-
assert subject.foo != before_foo
|
31
|
-
assert subject.bar != before_bar
|
24
|
+
test 'raises error if object not respond to the given method' do |subject|
|
25
|
+
assert_raise(NameError) { stub(subject, :nan, nil) }
|
32
26
|
end
|
33
27
|
|
34
|
-
test '
|
28
|
+
test 'stubbed method return new value' do |subject|
|
35
29
|
before = subject.foo
|
36
30
|
|
37
|
-
stub subject, foo
|
31
|
+
stub subject, :foo, 'new foo' do
|
32
|
+
assert_equal 'new foo', subject.foo
|
33
|
+
end
|
38
34
|
|
39
|
-
|
40
|
-
assert_equal 'new foo', subject.foo
|
35
|
+
assert_equal before, subject.foo
|
41
36
|
end
|
42
37
|
|
43
|
-
test 'stubs method with a callable object
|
38
|
+
test 'stubs method with a callable object' do |subject|
|
44
39
|
before = subject.foo
|
45
40
|
|
46
|
-
stub subject, foo
|
41
|
+
stub subject, :foo, -> { 'new foo' } do
|
42
|
+
assert_equal 'new foo', subject.foo
|
43
|
+
end
|
47
44
|
|
48
|
-
|
49
|
-
assert_equal 'new foo', subject.foo('foo')
|
45
|
+
assert_equal before, subject.foo
|
50
46
|
end
|
51
47
|
|
52
|
-
test 'stubs method
|
53
|
-
before =
|
48
|
+
test 'stubs method with a callable object that requires arguments' do |subject|
|
49
|
+
before = subject.foo
|
54
50
|
|
55
|
-
stub
|
56
|
-
|
51
|
+
stub subject, :foo, ->(a) { "new #{a}" } do
|
52
|
+
assert_equal 'new foo', subject.foo('foo')
|
57
53
|
end
|
58
54
|
|
59
|
-
assert_equal before,
|
55
|
+
assert_equal before, subject.foo
|
60
56
|
end
|
61
57
|
|
62
58
|
test 'succeeds if expectations are met' do |subject|
|
63
|
-
expect subject, :baz, with: ['value'],
|
59
|
+
expect subject, :baz, with: ['value'], returns: 'result' do
|
60
|
+
assert_equal 'result', subject.baz('value')
|
61
|
+
end
|
64
62
|
|
65
|
-
assert_equal '
|
63
|
+
assert_equal 'baz', subject.baz('baz')
|
66
64
|
end
|
67
65
|
|
68
66
|
test 'raises an error if expectation are not met' do |subject|
|
69
|
-
expect subject, :baz, with: ['value'],
|
70
|
-
|
71
|
-
assert_raise(Mocoso::ExpectationError) { subject.baz('another') }
|
72
|
-
end
|
73
|
-
|
74
|
-
test 'expectation without side effects if a block is given' do |subject|
|
75
|
-
expect subject, :baz, with: ['value'], return: 'mocked' do
|
76
|
-
assert_equal 'mocked', subject.baz('value')
|
67
|
+
expect subject, :baz, with: ['value'], returns: 'result' do
|
68
|
+
assert_raise(Mocoso::ExpectationError) { subject.baz('another') }
|
77
69
|
end
|
78
|
-
|
79
|
-
assert_equal 'original', subject.baz('original')
|
80
70
|
end
|
81
71
|
|
82
|
-
test 'expectation without
|
83
|
-
expect subject, :foo,
|
84
|
-
|
85
|
-
|
72
|
+
test 'expectation without with option defaults to empty array' do |subject|
|
73
|
+
expect subject, :foo, returns: 'new foo' do
|
74
|
+
assert_equal 'new foo', subject.foo
|
75
|
+
end
|
86
76
|
end
|
87
77
|
|
88
78
|
test 'expectation with multiple arguments' do |subject|
|
89
|
-
expect subject, :foo, with: ['new foo', { optional: true }],
|
90
|
-
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
test 'unstub removes specified stubbed methods' do |subject|
|
95
|
-
before_foo = subject.foo
|
96
|
-
before_bar = subject.bar
|
97
|
-
|
98
|
-
stub subject, foo: 'new foo', bar: 'new bar', baz: 'new baz'
|
99
|
-
|
100
|
-
assert before_foo != subject.foo
|
101
|
-
assert before_bar != subject.bar
|
102
|
-
|
103
|
-
unstub subject, [:foo, :bar]
|
104
|
-
|
105
|
-
assert_equal before_foo, subject.foo
|
106
|
-
assert_equal before_bar, subject.bar
|
107
|
-
assert_equal 'new baz', subject.baz
|
108
|
-
end
|
109
|
-
|
110
|
-
test 'unstub removes specified expectations' do
|
111
|
-
before = Subject.foo
|
112
|
-
|
113
|
-
expect Subject, :foo, return: 'new foo'
|
114
|
-
|
115
|
-
assert_equal 'new foo', Subject.foo
|
116
|
-
|
117
|
-
unstub Subject, [:foo]
|
118
|
-
|
119
|
-
assert_equal before, Subject.foo
|
79
|
+
expect subject, :foo, with: ['new foo', { optional: true }], returns: 'new foo' do
|
80
|
+
assert_equal 'new foo', subject.foo('new foo', optional: true)
|
81
|
+
end
|
120
82
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mocoso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Francesco Rodríguez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cutest
|