quacky 0.2.2 → 0.2.3
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.
- data/VERSION +1 -1
- data/lib/quacky.rb +5 -0
- data/lib/quacky/double.rb +14 -0
- data/lib/quacky/duck_type_verifier.rb +39 -0
- data/lib/quacky/expectations.rb +51 -0
- data/lib/quacky/minitest_setup.rb +32 -0
- data/lib/quacky/quacky.rb +2 -177
- data/lib/quacky/rspec_setup.rb +6 -0
- data/lib/quacky/stub.rb +76 -0
- data/spec/lib/double_spec.rb +9 -0
- data/spec/lib/duck_type_verifier_spec.rb +55 -0
- data/spec/lib/expectations_spec.rb +59 -0
- data/spec/lib/quacky/minitest_setup_spec.rb +14 -0
- data/spec/lib/quacky_spec.rb +1 -210
- data/spec/lib/stub_spec.rb +87 -0
- data/spec/spec_helper.rb +16 -0
- metadata +29 -12
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.3
|
data/lib/quacky.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Quacky
|
2
|
+
class DuckTypeVerificationFailure < RuntimeError; end
|
3
|
+
|
4
|
+
class DuckTypeVerifier
|
5
|
+
def initialize duck_type
|
6
|
+
@duck_type = duck_type
|
7
|
+
end
|
8
|
+
|
9
|
+
def verify! object
|
10
|
+
duck_type_methods.each do |method|
|
11
|
+
raise Quacky::DuckTypeVerificationFailure, "object does not respond to `#{method.name}'" unless object.respond_to?(method.name)
|
12
|
+
|
13
|
+
target_method = object.public_method(method.name)
|
14
|
+
if target_method.parameters.count != method.parameters.count ||
|
15
|
+
target_method.parameters.map {|p| p.first } != method.parameters.map {|p| p.first}
|
16
|
+
raise Quacky::DuckTypeVerificationFailure, "definitions of method `#{method.name}` differ in parameters accepted."
|
17
|
+
end
|
18
|
+
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
attr_reader :duck_type
|
25
|
+
|
26
|
+
def duck_type_methods
|
27
|
+
@duck_type_methods ||= (duck_type_object.methods - Object.methods).map do |method_name|
|
28
|
+
@duck_type_object.public_method(method_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def duck_type_object
|
33
|
+
return @duck_type_object if @duck_type_object
|
34
|
+
duck_type_class = Class.new
|
35
|
+
duck_type_class.send :include, duck_type
|
36
|
+
@duck_type_object = duck_type_class.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_support/core_ext/module/aliasing'
|
2
|
+
|
3
|
+
module Quacky
|
4
|
+
class NoMethodError < RuntimeError; end
|
5
|
+
|
6
|
+
module Expectations
|
7
|
+
def quacky_stub method_name
|
8
|
+
setup_expectation method_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def should_receive method_name
|
12
|
+
quacky_stub(method_name).tap do |expectation|
|
13
|
+
Quacky.expectations << expectation
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def quacky_expectations
|
20
|
+
@expectations ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup_expectation method_name
|
24
|
+
method_name = method_name.to_sym
|
25
|
+
raise Quacky::NoMethodError unless respond_to? method_name
|
26
|
+
|
27
|
+
quacky_expectations[method_name] = Stub.new(public_method(method_name))
|
28
|
+
sanitized_name, postpend = parse_method_name method_name
|
29
|
+
|
30
|
+
eval <<-EVAL
|
31
|
+
class << self
|
32
|
+
define_method("#{sanitized_name}_with_expectation#{postpend}") do |*args|
|
33
|
+
quacky_expectations[:#{method_name}].call *args
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method_chain :#{method_name}, :expectation
|
37
|
+
end
|
38
|
+
EVAL
|
39
|
+
|
40
|
+
quacky_expectations[method_name]
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_method_name method_name
|
44
|
+
method_name = method_name.to_s
|
45
|
+
eol_matcher = /([\!\?])$/
|
46
|
+
method_name_postpend = method_name.to_s.match(eol_matcher) ? $1 : ""
|
47
|
+
method_name_minus_postpend = method_name.to_s.gsub eol_matcher, ""
|
48
|
+
[method_name_minus_postpend, method_name_postpend]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
if defined? MiniTest
|
2
|
+
module Quacky
|
3
|
+
module MiniTest
|
4
|
+
module Matchers
|
5
|
+
def assert_quacks_like object, *modules
|
6
|
+
modules.each do |a_module|
|
7
|
+
Quacky::DuckTypeVerifier.new(a_module).verify! object
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Quacky
|
15
|
+
alias :mock :double
|
16
|
+
alias :class_mock :class_double
|
17
|
+
|
18
|
+
module Expectations
|
19
|
+
def expect method_name, return_value, with=[]
|
20
|
+
should_receive(method_name).and_return(return_value).tap do |expectation|
|
21
|
+
expectation.with(*with) unless with.empty?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def stub method_name, return_value, with=[]
|
26
|
+
quacky_stub(method_name).and_return(return_value).tap do |expectation|
|
27
|
+
expectation.with(*with) unless with.empty?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/quacky/quacky.rb
CHANGED
@@ -1,66 +1,6 @@
|
|
1
|
-
require 'active_support/core_ext/module/aliasing'
|
2
|
-
|
3
|
-
module Quacky
|
4
|
-
class DuckTypeVerificationFailure < RuntimeError; end
|
5
|
-
|
6
|
-
class DuckTypeVerifier
|
7
|
-
def initialize duck_type
|
8
|
-
@duck_type = duck_type
|
9
|
-
end
|
10
|
-
|
11
|
-
def verify! object
|
12
|
-
duck_type_methods.each do |method|
|
13
|
-
raise Quacky::DuckTypeVerificationFailure, "object does not respond to `#{method.name}'" unless object.respond_to?(method.name)
|
14
|
-
|
15
|
-
target_method = object.public_method(method.name)
|
16
|
-
if target_method.parameters.count != method.parameters.count ||
|
17
|
-
target_method.parameters.map {|p| p.first } != method.parameters.map {|p| p.first}
|
18
|
-
raise Quacky::DuckTypeVerificationFailure, "definitions of method `#{method.name}` differ in parameters accepted."
|
19
|
-
end
|
20
|
-
|
21
|
-
true
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
attr_reader :duck_type
|
27
|
-
|
28
|
-
def duck_type_methods
|
29
|
-
@duck_type_methods ||= (duck_type_object.methods - Object.methods).map do |method_name|
|
30
|
-
@duck_type_object.public_method(method_name)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def duck_type_object
|
35
|
-
return @duck_type_object if @duck_type_object
|
36
|
-
duck_type_class = Class.new
|
37
|
-
duck_type_class.send :include, duck_type
|
38
|
-
@duck_type_object = duck_type_class.new
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
1
|
module Quacky
|
44
2
|
extend self
|
45
3
|
|
46
|
-
class Double
|
47
|
-
def initialize(name)
|
48
|
-
@name = name
|
49
|
-
end
|
50
|
-
|
51
|
-
def inspect
|
52
|
-
"<Quacky::Double :#{name}>"
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
attr_reader :name
|
57
|
-
end
|
58
|
-
|
59
|
-
class NoMethodError < RuntimeError; end
|
60
|
-
class MethodSignatureMismatch < ArgumentError; end
|
61
|
-
class UnexpectedArguments < ArgumentError; end
|
62
|
-
class UnsatisfiedExpectation < ArgumentError; end
|
63
|
-
|
64
4
|
def expectations
|
65
5
|
@expectations ||= []
|
66
6
|
end
|
@@ -69,128 +9,12 @@ module Quacky
|
|
69
9
|
@expectations = nil
|
70
10
|
end
|
71
11
|
|
72
|
-
class Stub
|
73
|
-
def initialize method
|
74
|
-
@method = method
|
75
|
-
end
|
76
|
-
|
77
|
-
def with *args
|
78
|
-
@expected_args = args
|
79
|
-
call_through *args
|
80
|
-
self
|
81
|
-
end
|
82
|
-
|
83
|
-
def and_return value=nil, &block
|
84
|
-
@return_value = value
|
85
|
-
@return_block = block
|
86
|
-
end
|
87
|
-
|
88
|
-
def call *args
|
89
|
-
@called_args = args
|
90
|
-
validate_expectation
|
91
|
-
call_through *args
|
92
|
-
|
93
|
-
if expected_args
|
94
|
-
return_value if (called_args == expected_args)
|
95
|
-
else
|
96
|
-
return_value
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def validate_satisfaction!
|
101
|
-
if expected_args
|
102
|
-
if called_args == expected_args
|
103
|
-
true
|
104
|
-
else
|
105
|
-
raise UnsatisfiedExpectation
|
106
|
-
end
|
107
|
-
else
|
108
|
-
was_called? || raise(UnsatisfiedExpectation, "Expected `#{@method.name}` to be called.")
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
attr_reader :called_args, :expected_args
|
114
|
-
|
115
|
-
def was_called?
|
116
|
-
!!called_args
|
117
|
-
end
|
118
|
-
|
119
|
-
def validate_expectation
|
120
|
-
if expected_args && called_args != expected_args
|
121
|
-
raise(
|
122
|
-
Quacky::UnexpectedArguments,
|
123
|
-
"#{@method.name} was called with unexpected arguments: #{called_args.map(&:inspect).join ", "}. expected: #{expected_args.map(&:inspect).join ", "}"
|
124
|
-
)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def return_value
|
129
|
-
return @return_value if @return_value
|
130
|
-
@return_block.call if @return_block
|
131
|
-
end
|
132
|
-
|
133
|
-
def call_through *args
|
134
|
-
begin
|
135
|
-
@method.call *args
|
136
|
-
rescue ArgumentError => e
|
137
|
-
raise Quacky::MethodSignatureMismatch, "#{@method.receiver}##{@method.name} was called with the #{e.message}"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
module Expectations
|
143
|
-
def stub method_name
|
144
|
-
setup_expectation method_name
|
145
|
-
end
|
146
|
-
|
147
|
-
def should_receive method_name
|
148
|
-
stub(method_name).tap do |expectation|
|
149
|
-
Quacky.expectations << expectation
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
private
|
154
|
-
|
155
|
-
def quacky_expectations
|
156
|
-
@expectations ||= {}
|
157
|
-
end
|
158
|
-
|
159
|
-
def setup_expectation method_name
|
160
|
-
method_name = method_name.to_sym
|
161
|
-
raise Quacky::NoMethodError unless respond_to? method_name
|
162
|
-
|
163
|
-
quacky_expectations[method_name] = Stub.new(public_method(method_name))
|
164
|
-
sanitized_name, postpend = parse_method_name method_name
|
165
|
-
|
166
|
-
eval <<-EVAL
|
167
|
-
class << self
|
168
|
-
define_method("#{sanitized_name}_with_expectation#{postpend}") do |*args|
|
169
|
-
quacky_expectations[:#{method_name}].call *args
|
170
|
-
end
|
171
|
-
|
172
|
-
alias_method_chain :#{method_name}, :expectation
|
173
|
-
end
|
174
|
-
EVAL
|
175
|
-
|
176
|
-
quacky_expectations[method_name]
|
177
|
-
end
|
178
|
-
|
179
|
-
def parse_method_name method_name
|
180
|
-
method_name = method_name.to_s
|
181
|
-
eol_matcher = /([\!\?])$/
|
182
|
-
method_name_postpend = method_name.to_s.match(eol_matcher) ? $1 : ""
|
183
|
-
method_name_minus_postpend = method_name.to_s.gsub eol_matcher, ""
|
184
|
-
[method_name_minus_postpend, method_name_postpend]
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
12
|
def double(name, *duck_types)
|
189
13
|
Double.new(name).tap do |object|
|
190
14
|
duck_types.each do |duck_type|
|
191
15
|
object.extend duck_type
|
192
16
|
end
|
193
|
-
object.extend
|
17
|
+
object.extend Expectations
|
194
18
|
end
|
195
19
|
end
|
196
20
|
|
@@ -216,6 +40,7 @@ module Quacky
|
|
216
40
|
class_modules, instance_modules = parse_class_double_options options
|
217
41
|
|
218
42
|
Class.new do
|
43
|
+
extend Expectations
|
219
44
|
extend ClassInspect
|
220
45
|
include InstanceInspect
|
221
46
|
name_class_double name
|
data/lib/quacky/rspec_setup.rb
CHANGED
@@ -2,6 +2,12 @@ if defined? RSpec
|
|
2
2
|
require 'rspec'
|
3
3
|
require 'rspec/matchers'
|
4
4
|
|
5
|
+
module Quacky
|
6
|
+
module Expectations
|
7
|
+
alias :stub :quacky_stub
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
5
11
|
RSpec::Matchers.define :quack_like do |*expected_duck_types|
|
6
12
|
expected_duck_types.each do |expected_duck_type|
|
7
13
|
match do |actual|
|
data/lib/quacky/stub.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Quacky
|
2
|
+
class UnexpectedArguments < ArgumentError; end
|
3
|
+
class UnsatisfiedExpectation < ArgumentError; end
|
4
|
+
class MethodSignatureMismatch < ArgumentError; end
|
5
|
+
|
6
|
+
class Stub
|
7
|
+
def initialize method
|
8
|
+
@method = method
|
9
|
+
end
|
10
|
+
|
11
|
+
def with *args
|
12
|
+
@expected_args = args
|
13
|
+
call_through *args
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def and_return value=nil, &block
|
18
|
+
@return_value = value
|
19
|
+
@return_block = block
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def call *args
|
24
|
+
@called_args = args
|
25
|
+
validate_expectation
|
26
|
+
call_through *args
|
27
|
+
|
28
|
+
if expected_args
|
29
|
+
return_value if (called_args == expected_args)
|
30
|
+
else
|
31
|
+
return_value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_satisfaction!
|
36
|
+
if expected_args
|
37
|
+
if called_args == expected_args
|
38
|
+
true
|
39
|
+
else
|
40
|
+
raise UnsatisfiedExpectation
|
41
|
+
end
|
42
|
+
else
|
43
|
+
was_called? || raise(UnsatisfiedExpectation, "Expected `#{@method.name}` to be called.")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
attr_reader :called_args, :expected_args
|
49
|
+
|
50
|
+
def was_called?
|
51
|
+
!!called_args
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_expectation
|
55
|
+
if expected_args && called_args != expected_args
|
56
|
+
raise(
|
57
|
+
Quacky::UnexpectedArguments,
|
58
|
+
"#{@method.name} was called with unexpected arguments: #{called_args.map(&:inspect).join ", "}. expected: #{expected_args.map(&:inspect).join ", "}"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def return_value
|
64
|
+
return @return_value if @return_value
|
65
|
+
@return_block.call if @return_block
|
66
|
+
end
|
67
|
+
|
68
|
+
def call_through *args
|
69
|
+
begin
|
70
|
+
@method.call *args
|
71
|
+
rescue ArgumentError => e
|
72
|
+
raise Quacky::MethodSignatureMismatch, "#{@method.receiver}##{@method.name} was called with the #{e.message}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Quacky::DuckTypeVerifier do
|
4
|
+
let(:conforming_object) do
|
5
|
+
Class.new do
|
6
|
+
def quack arg1,arg2,arg3=nil; end
|
7
|
+
end.new
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:duck_type_module) do
|
11
|
+
Module.new do
|
12
|
+
def quack a,b,c=nil; end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#verify!" do
|
17
|
+
let(:verifier) { Quacky::DuckTypeVerifier.new(duck_type_module) }
|
18
|
+
|
19
|
+
context "non-conforming objects" do
|
20
|
+
context "an object that doesn't even respond to the same methods" do
|
21
|
+
let(:non_conforming_object) do
|
22
|
+
Class.new.new
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should raise a Quacky::DuckTypeVerificationFailure" do
|
26
|
+
expect { verifier.verify! non_conforming_object }.to raise_exception Quacky::DuckTypeVerificationFailure
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "an object that has the methods but with different parameters" do
|
31
|
+
let(:non_conforming_object) do
|
32
|
+
Class.new do
|
33
|
+
def quack; end
|
34
|
+
end.new
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise a Quacky::DuckTypeVerificationFailure" do
|
38
|
+
expect { verifier.verify! non_conforming_object }.to raise_exception Quacky::DuckTypeVerificationFailure, "definitions of method `quack` differ in parameters accepted."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "given a conforming object" do
|
44
|
+
let(:conforming_object) do
|
45
|
+
Class.new do
|
46
|
+
def quack arg1,arg2,arg3=nil; end
|
47
|
+
end.new
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return true" do
|
51
|
+
expect { verifier.verify! conforming_object }.not_to raise_exception Quacky::DuckTypeVerificationFailure
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Quacky::Double created with Quacky.double" do
|
4
|
+
let(:q_double) { Quacky.double :duck, Duck }
|
5
|
+
let(:expectation) { double(:expectation) }
|
6
|
+
|
7
|
+
describe "#stub" do
|
8
|
+
it "should raise an exception if the method does not already exist on the double" do
|
9
|
+
expect { q_double.quacky_stub("random_method_#{rand 1000000}") }.to raise_exception Quacky::NoMethodError
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should initialize and return a new Quacky::Stub otherwise" do
|
13
|
+
Quacky::Stub.should_receive(:new).with(q_double.public_method(:duck!)).and_return expectation
|
14
|
+
q_double.quacky_stub(:duck!).should == expectation
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should reroute calls to the original method to call the expectation's call method" do
|
18
|
+
Quacky::Stub.stub(:new).with(q_double.public_method(:duck!)).and_return expectation
|
19
|
+
q_double.quacky_stub(:duck!)
|
20
|
+
|
21
|
+
argument = double :argument
|
22
|
+
expectation.should_receive(:call).with argument
|
23
|
+
q_double.duck!(argument)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should support methods ending in !, ?, and regular letters" do
|
27
|
+
q_double = Quacky.double(:quacky_double, Module.new do
|
28
|
+
def bang!; end
|
29
|
+
def question?; end
|
30
|
+
def regular; end
|
31
|
+
end)
|
32
|
+
|
33
|
+
q_double.should_receive(:bang!).and_return "bang"
|
34
|
+
q_double.should_receive(:question?).and_return "question"
|
35
|
+
q_double.should_receive(:regular).and_return "regular"
|
36
|
+
|
37
|
+
q_double.bang!.should == "bang"
|
38
|
+
q_double.question?.should == "question"
|
39
|
+
q_double.regular.should == "regular"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#should_receive" do
|
44
|
+
it "should raise an exception if the method does not already exist on the quacky double" do
|
45
|
+
expect { q_double.should_receive("random_method_#{rand 1000000}") }.to raise_exception Quacky::NoMethodError
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should initialize and return a new QuackyStub otherwise" do
|
49
|
+
Quacky::Stub.should_receive(:new).with(q_double.public_method(:duck!)).and_return expectation
|
50
|
+
q_double.should_receive(:duck!).should == expectation
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should add the generated expectation to the list of required expectations" do
|
54
|
+
Quacky::Stub.stub(:new).with(q_double.public_method(:duck!)).and_return expectation
|
55
|
+
q_double.should_receive(:duck!)
|
56
|
+
Quacky.expectations.should include expectation
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module MiniTest; end
|
2
|
+
|
3
|
+
require_relative '../../../lib/quacky/minitest_setup'
|
4
|
+
require_relative '../../../lib/quacky/quacky'
|
5
|
+
|
6
|
+
describe Quacky do
|
7
|
+
describe ".mock" do
|
8
|
+
it "should be an alias for .double" do
|
9
|
+
Quacky.public_method(:mock).should == Quacky.public_method(:double)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Object.send(:remove_const, :MiniTest)
|
data/spec/lib/quacky_spec.rb
CHANGED
@@ -1,65 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Duck
|
4
|
-
def duck! arg; end
|
5
|
-
end
|
6
|
-
|
7
|
-
describe Quacky::DuckTypeVerifier do
|
8
|
-
let(:conforming_object) do
|
9
|
-
Class.new do
|
10
|
-
def quack arg1,arg2,arg3=nil; end
|
11
|
-
end.new
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:duck_type_module) do
|
15
|
-
Module.new do
|
16
|
-
def quack a,b,c=nil; end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe "#verify!" do
|
21
|
-
let(:verifier) { Quacky::DuckTypeVerifier.new(duck_type_module) }
|
22
|
-
|
23
|
-
context "non-conforming objects" do
|
24
|
-
context "an object that doesn't even respond to the same methods" do
|
25
|
-
let(:non_conforming_object) do
|
26
|
-
Class.new.new
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should raise a Quacky::DuckTypeVerificationFailure" do
|
30
|
-
expect { verifier.verify! non_conforming_object }.to raise_exception Quacky::DuckTypeVerificationFailure
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context "an object that has the methods but with different parameters" do
|
35
|
-
let(:non_conforming_object) do
|
36
|
-
Class.new do
|
37
|
-
def quack; end
|
38
|
-
end.new
|
39
|
-
end
|
40
|
-
|
41
|
-
it "should raise a Quacky::DuckTypeVerificationFailure" do
|
42
|
-
expect { verifier.verify! non_conforming_object }.to raise_exception Quacky::DuckTypeVerificationFailure, "definitions of method `quack` differ in parameters accepted."
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context "given a conforming object" do
|
48
|
-
let(:conforming_object) do
|
49
|
-
Class.new do
|
50
|
-
def quack arg1,arg2,arg3=nil; end
|
51
|
-
end.new
|
52
|
-
end
|
53
|
-
|
54
|
-
it "should return true" do
|
55
|
-
expect { verifier.verify! conforming_object }.not_to raise_exception Quacky::DuckTypeVerificationFailure
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
1
|
+
require 'spec_helper'
|
60
2
|
|
61
3
|
describe Quacky do
|
62
|
-
|
63
4
|
describe ".clear_expectations!" do
|
64
5
|
it "should reset .expecatations to an empty collection" do
|
65
6
|
Quacky.expectations.should be_empty
|
@@ -132,154 +73,4 @@ describe Quacky do
|
|
132
73
|
it_behaves_like "quacky class double"
|
133
74
|
end
|
134
75
|
end
|
135
|
-
|
136
|
-
describe Quacky::Double do
|
137
|
-
let(:q_double) { Quacky.double :duck, Duck }
|
138
|
-
let(:expectation) { double(:expectation) }
|
139
|
-
|
140
|
-
describe ".inspect" do
|
141
|
-
it "should be formatted like `<Quacky::Double :<double_name>>`" do
|
142
|
-
Quacky::Double.new(:test).inspect.should == "<Quacky::Double :test>"
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
describe ".stub" do
|
147
|
-
it "should raise an exception if the method does not already exist on the double" do
|
148
|
-
expect { q_double.stub("random_method_#{rand 1000000}") }.to raise_exception Quacky::NoMethodError
|
149
|
-
end
|
150
|
-
|
151
|
-
it "should initialize and return a new Quacky::Stub otherwise" do
|
152
|
-
Quacky::Stub.should_receive(:new).with(q_double.public_method(:duck!)).and_return expectation
|
153
|
-
q_double.stub(:duck!).should == expectation
|
154
|
-
end
|
155
|
-
|
156
|
-
it "should reroute calls to the original method to call the expectation's call method" do
|
157
|
-
Quacky::Stub.stub(:new).with(q_double.public_method(:duck!)).and_return expectation
|
158
|
-
q_double.stub(:duck!)
|
159
|
-
|
160
|
-
argument = double :argument
|
161
|
-
expectation.should_receive(:call).with argument
|
162
|
-
q_double.duck!(argument)
|
163
|
-
end
|
164
|
-
|
165
|
-
it "should support methods ending in !, ?, and regular letters" do
|
166
|
-
q_double = Quacky.double(:quacky_double, Module.new do
|
167
|
-
def bang!; end
|
168
|
-
def question?; end
|
169
|
-
def regular; end
|
170
|
-
end)
|
171
|
-
|
172
|
-
q_double.should_receive(:bang!).and_return "bang"
|
173
|
-
q_double.should_receive(:question?).and_return "question"
|
174
|
-
q_double.should_receive(:regular).and_return "regular"
|
175
|
-
|
176
|
-
q_double.bang!.should == "bang"
|
177
|
-
q_double.question?.should == "question"
|
178
|
-
q_double.regular.should == "regular"
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
describe "should_receive" do
|
183
|
-
it "should raise an exception if the method does not already exist on the quacky double" do
|
184
|
-
expect { q_double.should_receive("random_method_#{rand 1000000}") }.to raise_exception Quacky::NoMethodError
|
185
|
-
end
|
186
|
-
|
187
|
-
it "should initialize and return a new QuackyStub otherwise" do
|
188
|
-
Quacky::Stub.should_receive(:new).with(q_double.public_method(:duck!)).and_return expectation
|
189
|
-
q_double.should_receive(:duck!).should == expectation
|
190
|
-
end
|
191
|
-
|
192
|
-
it "should add the generated expectation to the list of required expectations" do
|
193
|
-
Quacky::Stub.stub(:new).with(q_double.public_method(:duck!)).and_return expectation
|
194
|
-
q_double.should_receive(:duck!)
|
195
|
-
Quacky.expectations.should include expectation
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
module Quacky
|
202
|
-
describe "mocks" do
|
203
|
-
let(:object) do
|
204
|
-
Class.new do
|
205
|
-
include Duck
|
206
|
-
end.new
|
207
|
-
end
|
208
|
-
|
209
|
-
describe Stub do
|
210
|
-
let(:q_expectation) { Quacky::Stub.new(object.public_method(:duck!)) }
|
211
|
-
|
212
|
-
describe "#with" do
|
213
|
-
it "should raise an exception if the original method's signature mismatches" do
|
214
|
-
expect { q_expectation.with 1,2,3 }.to raise_exception Quacky::MethodSignatureMismatch, "#{object.inspect}#duck! was called with the wrong number of arguments (3 for 1)"
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
describe "#and_return" do
|
219
|
-
context "static value" do
|
220
|
-
it "should return the static value when called" do
|
221
|
-
return_value = double :return_value
|
222
|
-
q_expectation.and_return return_value
|
223
|
-
q_expectation.call(double :argument).should == return_value
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
context "block return value" do
|
228
|
-
it "should return the value returned by the block" do
|
229
|
-
return_value = double :return_value
|
230
|
-
q_expectation.and_return { return_value }
|
231
|
-
q_expectation.call(double :argument).should == return_value
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
describe "#call" do
|
237
|
-
subject { q_expectation }
|
238
|
-
|
239
|
-
context "with invalid arguments" do
|
240
|
-
it "should raise an exception" do
|
241
|
-
expect{ q_expectation.call(1,2,3)}.to raise_exception Quacky::MethodSignatureMismatch
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
context "called with unexpected arguments" do
|
246
|
-
it "should raise a Quacky::UnexpectedArguments exception" do
|
247
|
-
q_expectation.with double(:expected_argument)
|
248
|
-
expect { q_expectation.call double(:unexpected_arguments) }.to raise_exception Quacky::UnexpectedArguments
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
context "called with expected arguments" do
|
253
|
-
it "should return the configured return value" do
|
254
|
-
q_expectation.with("foo").and_return "bar"
|
255
|
-
q_expectation.call("foo").should == "bar"
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
describe "validate_satisfaction!" do
|
261
|
-
subject { q_expectation.validate_satisfaction! }
|
262
|
-
|
263
|
-
context "no with expectation" do
|
264
|
-
before { q_expectation.call double(:argument) }
|
265
|
-
specify { expect { subject }.not_to raise_exception }
|
266
|
-
end
|
267
|
-
|
268
|
-
context "not called at all" do
|
269
|
-
specify { expect { subject }.to raise_exception Quacky::UnsatisfiedExpectation, "Expected `duck!` to be called." }
|
270
|
-
end
|
271
|
-
|
272
|
-
context "with expectation" do
|
273
|
-
let(:argument) { double :argument }
|
274
|
-
|
275
|
-
before { q_expectation.with argument }
|
276
|
-
|
277
|
-
context "called with matching argument" do
|
278
|
-
before { q_expectation.call argument }
|
279
|
-
specify { expect { subject }.not_to raise_exception }
|
280
|
-
end
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
76
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Quacky
|
4
|
+
describe "mocks" do
|
5
|
+
let(:object) do
|
6
|
+
Class.new do
|
7
|
+
include Duck
|
8
|
+
end.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Stub do
|
12
|
+
let(:q_expectation) { Quacky::Stub.new(object.public_method(:duck!)) }
|
13
|
+
|
14
|
+
describe "#with" do
|
15
|
+
it "should raise an exception if the original method's signature mismatches" do
|
16
|
+
expect { q_expectation.with 1,2,3 }.to raise_exception Quacky::MethodSignatureMismatch, "#{object.inspect}#duck! was called with the wrong number of arguments (3 for 1)"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#and_return" do
|
21
|
+
context "static value" do
|
22
|
+
it "should return the static value when called" do
|
23
|
+
return_value = double :return_value
|
24
|
+
q_expectation.and_return return_value
|
25
|
+
q_expectation.call(double :argument).should == return_value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "block return value" do
|
30
|
+
it "should return the value returned by the block" do
|
31
|
+
return_value = double :return_value
|
32
|
+
q_expectation.and_return { return_value }
|
33
|
+
q_expectation.call(double :argument).should == return_value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#call" do
|
39
|
+
subject { q_expectation }
|
40
|
+
|
41
|
+
context "with invalid arguments" do
|
42
|
+
it "should raise an exception" do
|
43
|
+
expect{ q_expectation.call(1,2,3)}.to raise_exception Quacky::MethodSignatureMismatch
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "called with unexpected arguments" do
|
48
|
+
it "should raise a Quacky::UnexpectedArguments exception" do
|
49
|
+
q_expectation.with double(:expected_argument)
|
50
|
+
expect { q_expectation.call double(:unexpected_arguments) }.to raise_exception Quacky::UnexpectedArguments
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "called with expected arguments" do
|
55
|
+
it "should return the configured return value" do
|
56
|
+
q_expectation.with("foo").and_return "bar"
|
57
|
+
q_expectation.call("foo").should == "bar"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "validate_satisfaction!" do
|
63
|
+
subject { q_expectation.validate_satisfaction! }
|
64
|
+
|
65
|
+
context "no with expectation" do
|
66
|
+
before { q_expectation.call double(:argument) }
|
67
|
+
specify { expect { subject }.not_to raise_exception }
|
68
|
+
end
|
69
|
+
|
70
|
+
context "not called at all" do
|
71
|
+
specify { expect { subject }.to raise_exception Quacky::UnsatisfiedExpectation, "Expected `duck!` to be called." }
|
72
|
+
end
|
73
|
+
|
74
|
+
context "with expectation" do
|
75
|
+
let(:argument) { double :argument }
|
76
|
+
|
77
|
+
before { q_expectation.with argument }
|
78
|
+
|
79
|
+
context "called with matching argument" do
|
80
|
+
before { q_expectation.call argument }
|
81
|
+
specify { expect { subject }.not_to raise_exception }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift '.'
|
2
|
+
require 'quacky/quacky'
|
3
|
+
require 'quacky/stub'
|
4
|
+
require 'quacky/double'
|
5
|
+
require 'quacky/expectations'
|
6
|
+
require 'quacky/duck_type_verifier'
|
7
|
+
|
8
|
+
module Duck
|
9
|
+
def duck! arg; end
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec.configure do |c|
|
13
|
+
c.before do
|
14
|
+
Quacky.clear_expectations!
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quacky
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
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-07-
|
12
|
+
date: 2012-07-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &70238192808080 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.2.5
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70238192808080
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70238192807680 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70238192807680
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: cucumber
|
38
|
-
requirement: &
|
38
|
+
requirement: &70238192805940 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70238192805940
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: specdown
|
49
|
-
requirement: &
|
49
|
+
requirement: &70238192805520 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70238192805520
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rake
|
60
|
-
requirement: &
|
60
|
+
requirement: &70238192805080 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,18 +65,29 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70238192805080
|
69
69
|
description:
|
70
70
|
email: moonmaster9000@gmail.com
|
71
71
|
executables: []
|
72
72
|
extensions: []
|
73
73
|
extra_rdoc_files: []
|
74
74
|
files:
|
75
|
+
- lib/quacky/double.rb
|
76
|
+
- lib/quacky/duck_type_verifier.rb
|
77
|
+
- lib/quacky/expectations.rb
|
78
|
+
- lib/quacky/minitest_setup.rb
|
75
79
|
- lib/quacky/quacky.rb
|
76
80
|
- lib/quacky/rspec_setup.rb
|
81
|
+
- lib/quacky/stub.rb
|
77
82
|
- lib/quacky.rb
|
78
83
|
- VERSION
|
84
|
+
- spec/lib/double_spec.rb
|
85
|
+
- spec/lib/duck_type_verifier_spec.rb
|
86
|
+
- spec/lib/expectations_spec.rb
|
87
|
+
- spec/lib/quacky/minitest_setup_spec.rb
|
79
88
|
- spec/lib/quacky_spec.rb
|
89
|
+
- spec/lib/stub_spec.rb
|
90
|
+
- spec/spec_helper.rb
|
80
91
|
homepage: https://github.com/moonmaster9000/quacky
|
81
92
|
licenses: []
|
82
93
|
post_install_message:
|
@@ -102,4 +113,10 @@ signing_key:
|
|
102
113
|
specification_version: 3
|
103
114
|
summary: Ensure your test doubles quack like the real thing.
|
104
115
|
test_files:
|
116
|
+
- spec/lib/double_spec.rb
|
117
|
+
- spec/lib/duck_type_verifier_spec.rb
|
118
|
+
- spec/lib/expectations_spec.rb
|
119
|
+
- spec/lib/quacky/minitest_setup_spec.rb
|
105
120
|
- spec/lib/quacky_spec.rb
|
121
|
+
- spec/lib/stub_spec.rb
|
122
|
+
- spec/spec_helper.rb
|