gimme 0.1.8 → 0.2.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.
- data/Gemfile +3 -1
- data/Gemfile.lock +10 -4
- data/README.markdown +41 -41
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/features/step_definitions/gimme_steps.rb +15 -1
- data/features/stub_class_methods.feature +21 -0
- data/features/support/animals.rb +16 -8
- data/gimme.gemspec +22 -6
- data/lib/gimme.rb +7 -3
- data/lib/gimme/dsl.rb +50 -0
- data/lib/gimme/gives.rb +1 -10
- data/lib/gimme/gives_class_methods.rb +54 -0
- data/lib/gimme/invokes_satisfied_stubbing.rb +29 -0
- data/lib/gimme/reset.rb +14 -0
- data/lib/gimme/resolves_methods.rb +40 -0
- data/lib/gimme/rspec_adapter.rb +1 -0
- data/lib/gimme/test_double.rb +2 -36
- data/lib/gimme/verifies.rb +1 -10
- data/spec/gimme/captor_spec.rb +23 -22
- data/spec/gimme/errors_spec.rb +4 -2
- data/spec/gimme/gives_class_methods_spec.rb +51 -0
- data/spec/gimme/gives_spec.rb +25 -38
- data/spec/gimme/matchers_spec.rb +89 -88
- data/spec/gimme/resolves_methods_spec.rb +73 -0
- data/spec/gimme/rspec_adapter_spec.rb +14 -0
- data/spec/gimme/shared_examples/shared_gives_examples.rb +34 -0
- data/spec/gimme/test_double_spec.rb +18 -16
- data/spec/gimme/verifies_spec.rb +92 -91
- metadata +53 -29
- data/lib/gimme/method_resolver.rb +0 -33
data/spec/gimme/matchers_spec.rb
CHANGED
@@ -6,120 +6,121 @@ RSpec::Matchers.define :match do |expected|
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
class BabyShoopuf < Shoopuf
|
14
|
-
end
|
15
|
-
|
16
|
-
describe Matcher do
|
17
|
-
context "a plain, default matcher" do
|
18
|
-
Given(:matcher) { Gimme::Matchers::Matcher.new }
|
19
|
-
Then { matcher.should_not match "anything" }
|
9
|
+
module Gimme
|
10
|
+
describe Matchers do
|
11
|
+
class Shoopuf
|
20
12
|
end
|
21
|
-
end
|
22
13
|
|
23
|
-
|
24
|
-
shared_examples_for "an anything matcher" do
|
25
|
-
Then { matcher.should match "anything" }
|
14
|
+
class BabyShoopuf < Shoopuf
|
26
15
|
end
|
27
16
|
|
28
|
-
|
29
|
-
|
30
|
-
Given(:matcher) {
|
17
|
+
describe Matcher do
|
18
|
+
context "a plain, default matcher" do
|
19
|
+
Given(:matcher) { Matchers::Matcher.new }
|
20
|
+
Then { matcher.should_not match "anything" }
|
31
21
|
end
|
32
22
|
end
|
33
23
|
|
34
|
-
|
35
|
-
|
36
|
-
|
24
|
+
describe Anything do
|
25
|
+
shared_examples_for "an anything matcher" do
|
26
|
+
Then { matcher.should match "anything" }
|
37
27
|
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
shared_examples_for "an identity matcher" do
|
42
|
-
Then { matcher.should match Shoopuf.new }
|
43
|
-
Then { matcher.should match BabyShoopuf.new }
|
44
|
-
end
|
45
|
-
|
46
|
-
shared_examples_for "a restrictive matcher" do
|
47
|
-
Then { matcher.should_not match nil }
|
48
|
-
Then { matcher.should_not match "Pandas" }
|
49
|
-
Then { matcher.should_not match Object.new }
|
50
|
-
Then { matcher.should_not match Class }
|
51
|
-
end
|
52
|
-
|
53
|
-
shared_examples_for "a relaxed matcher" do
|
54
|
-
Then { matcher.should match nil }
|
55
|
-
end
|
56
28
|
|
29
|
+
context "class API" do
|
30
|
+
it_behaves_like "an anything matcher" do
|
31
|
+
Given(:matcher) { Anything.new }
|
32
|
+
end
|
33
|
+
end
|
57
34
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
35
|
+
context "gimme DSL" do
|
36
|
+
it_behaves_like "an anything matcher" do
|
37
|
+
Given(:matcher) { anything }
|
38
|
+
end
|
39
|
+
end
|
63
40
|
end
|
64
41
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
it_behaves_like "a restrictive matcher"
|
42
|
+
shared_examples_for "an identity matcher" do
|
43
|
+
Then { matcher.should match Shoopuf.new }
|
44
|
+
Then { matcher.should match BabyShoopuf.new }
|
69
45
|
end
|
70
|
-
end
|
71
46
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
47
|
+
shared_examples_for "a restrictive matcher" do
|
48
|
+
Then { matcher.should_not match nil }
|
49
|
+
Then { matcher.should_not match "Pandas" }
|
50
|
+
Then { matcher.should_not match Object.new }
|
51
|
+
Then { matcher.should_not match Class }
|
77
52
|
end
|
78
53
|
|
79
|
-
|
80
|
-
|
81
|
-
it_behaves_like "an identity matcher"
|
82
|
-
it_behaves_like "a relaxed matcher"
|
54
|
+
shared_examples_for "a relaxed matcher" do
|
55
|
+
Then { matcher.should match nil }
|
83
56
|
end
|
84
|
-
end
|
85
57
|
|
86
|
-
describe Gimme::Matchers::Numeric do
|
87
|
-
shared_examples_for "a numeric matcher" do
|
88
|
-
Then { matcher.should match 5 }
|
89
|
-
Then { matcher.should match 5.5 }
|
90
|
-
end
|
91
58
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
59
|
+
describe IsA do
|
60
|
+
context "class API" do
|
61
|
+
Given(:matcher) { IsA.new(Shoopuf) }
|
62
|
+
it_behaves_like "an identity matcher"
|
63
|
+
it_behaves_like "a restrictive matcher"
|
64
|
+
end
|
97
65
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
66
|
+
context "gimme DSL" do
|
67
|
+
Given(:matcher) { is_a(Shoopuf) }
|
68
|
+
it_behaves_like "an identity matcher"
|
69
|
+
it_behaves_like "a restrictive matcher"
|
70
|
+
end
|
102
71
|
end
|
103
|
-
end
|
104
72
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
73
|
+
describe Any do
|
74
|
+
context "class API" do
|
75
|
+
Given(:matcher) { Any.new(Shoopuf) }
|
76
|
+
it_behaves_like "an identity matcher"
|
77
|
+
it_behaves_like "a relaxed matcher"
|
78
|
+
end
|
79
|
+
|
80
|
+
context "gimme DSL" do
|
81
|
+
Given(:matcher) { any(Shoopuf) }
|
82
|
+
it_behaves_like "an identity matcher"
|
83
|
+
it_behaves_like "a relaxed matcher"
|
84
|
+
end
|
110
85
|
end
|
111
86
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
87
|
+
describe Matchers::Numeric do
|
88
|
+
shared_examples_for "a numeric matcher" do
|
89
|
+
Then { matcher.should match 5 }
|
90
|
+
Then { matcher.should match 5.5 }
|
91
|
+
end
|
92
|
+
|
93
|
+
context "class API" do
|
94
|
+
Given(:matcher) { Matchers::Numeric.new }
|
95
|
+
it_behaves_like "a numeric matcher"
|
96
|
+
it_behaves_like "a restrictive matcher"
|
97
|
+
end
|
98
|
+
|
99
|
+
context "gimme DSL" do
|
100
|
+
Given(:matcher) { numeric }
|
101
|
+
it_behaves_like "a numeric matcher"
|
102
|
+
it_behaves_like "a restrictive matcher"
|
103
|
+
end
|
116
104
|
end
|
117
105
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
106
|
+
describe Matchers::Boolean do
|
107
|
+
shared_examples_for "a boolean matcher" do
|
108
|
+
Then { matcher.should match true }
|
109
|
+
Then { matcher.should match false }
|
110
|
+
Then { matcher.should_not match Boolean }
|
111
|
+
end
|
112
|
+
|
113
|
+
context "class API" do
|
114
|
+
Given(:matcher) { Matchers::Boolean.new }
|
115
|
+
it_behaves_like "a boolean matcher"
|
116
|
+
it_behaves_like "a restrictive matcher"
|
117
|
+
end
|
118
|
+
|
119
|
+
context "gimme DSL" do
|
120
|
+
Given(:matcher) { boolean }
|
121
|
+
it_behaves_like "a boolean matcher"
|
122
|
+
it_behaves_like "a restrictive matcher"
|
123
|
+
end
|
122
124
|
end
|
123
125
|
end
|
124
|
-
|
125
126
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Berry
|
4
|
+
def ferment
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def grow
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Gimme
|
14
|
+
describe ResolvesMethods do
|
15
|
+
describe "#resolve" do
|
16
|
+
context "no class on the double" do
|
17
|
+
context "plain ol' method" do
|
18
|
+
subject { ResolvesMethods.new(nil,:foo,[]) }
|
19
|
+
Then { subject.resolve.should == :foo }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "using send" do
|
23
|
+
subject { ResolvesMethods.new(nil,:send,[:foo]) }
|
24
|
+
Then { subject.resolve.should == :foo }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "a class is provided" do
|
29
|
+
context "rigged to raise errors" do
|
30
|
+
context "method exists" do
|
31
|
+
subject { ResolvesMethods.new(Berry,:ferment,[]) }
|
32
|
+
Then { subject.resolve.should == :ferment }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "method does not exist" do
|
36
|
+
subject { ResolvesMethods.new(Berry,:fooberry,[]) }
|
37
|
+
When(:invocation) { lambda { subject.resolve } }
|
38
|
+
Then { invocation.should raise_error NoMethodError, /may not know how to respond/ }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "a private method" do
|
42
|
+
subject { ResolvesMethods.new(Berry,:grow,[]) }
|
43
|
+
When(:invocation) { lambda { subject.resolve } }
|
44
|
+
Then { invocation.should raise_error NoMethodError, /not be a great idea/ }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context "set up not to raise errors" do
|
50
|
+
context "method exists" do
|
51
|
+
subject { ResolvesMethods.new(Berry,:ferment,[]) }
|
52
|
+
When(:result) { subject.resolve(false) }
|
53
|
+
Then { result.should == :ferment }
|
54
|
+
end
|
55
|
+
|
56
|
+
context "method does not exist" do
|
57
|
+
subject { ResolvesMethods.new(Berry,:fooberry,[]) }
|
58
|
+
When(:result) { subject.resolve(false) }
|
59
|
+
Then { result.should == :fooberry }
|
60
|
+
end
|
61
|
+
|
62
|
+
context "a private methods" do
|
63
|
+
subject { ResolvesMethods.new(Berry,:grow,[]) }
|
64
|
+
When(:result) { subject.resolve(false) }
|
65
|
+
Then { result.should == :grow }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Gimme::RSpecAdapter
|
4
|
+
describe "subject" do
|
5
|
+
Then { setup_mocks_for_rspec }
|
6
|
+
Then { verify_mocks_for_rspec }
|
7
|
+
|
8
|
+
describe "#teardown_mocks_for_rspec" do
|
9
|
+
# Given { spy_on(Gimme).reset }
|
10
|
+
When { RSpecAdapter.teardown_mocks_for_rspec }
|
11
|
+
# Then { verify(Gimme).reset }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
shared_examples_for "a normal stubbing" do
|
2
|
+
describe "stubbing an existing method" do
|
3
|
+
context "no args" do
|
4
|
+
When { gives.nibble() { "nom" } }
|
5
|
+
Then { subject.nibble.should == "nom" }
|
6
|
+
end
|
7
|
+
|
8
|
+
context "with args" do
|
9
|
+
When { gives.eat("carrot") { "crunch" } }
|
10
|
+
Then { subject.eat("carrot").should == "crunch" }
|
11
|
+
Then { subject.eat("apple").should == nil }
|
12
|
+
# Then { lambda{ subject.eat }.should raise_error ArgumentError } # <PENDING
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with arg matchers" do
|
16
|
+
When { gives.eat(is_a(String)) { "yum" } }
|
17
|
+
Then { subject.eat("fooberry").should == "yum" }
|
18
|
+
Then { subject.eat(15).should == nil }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "stubbing a non-existent method" do
|
23
|
+
When(:stubbing) { lambda { gives.bark { "woof" } } }
|
24
|
+
Then { stubbing.should raise_error NoMethodError }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
shared_examples_for "an overridden stubbing" do
|
29
|
+
context "configured to _not_ raise an error when stubbed method does not exist" do
|
30
|
+
Given { gives.raises_no_method_error =false }
|
31
|
+
When { gives.bark { "woof" } }
|
32
|
+
Then { subject.bark.should == "woof" }
|
33
|
+
end
|
34
|
+
end
|
@@ -1,25 +1,27 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Gimme
|
4
|
+
describe TestDouble do
|
5
|
+
class MassiveDamage
|
6
|
+
def boom
|
7
|
+
:asplode
|
8
|
+
end
|
7
9
|
end
|
8
|
-
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
describe "#gimme_next" do
|
12
|
+
Given(:test_double) { gimme_next(MassiveDamage) }
|
13
|
+
Given(:subject) { MassiveDamage.new }
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
Given { give(test_double).boom { :kaboom } }
|
16
|
+
When(:result) { subject.boom }
|
17
|
+
Then { result.should == :kaboom }
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
context "subsequent uses" do
|
20
|
+
Given(:next_subject) { MassiveDamage.new }
|
21
|
+
When(:next_result) { next_subject.boom }
|
22
|
+
Then { next_result.should == :asplode }
|
23
|
+
end
|
22
24
|
end
|
23
|
-
end
|
24
25
|
|
26
|
+
end
|
25
27
|
end
|
data/spec/gimme/verifies_spec.rb
CHANGED
@@ -1,120 +1,121 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
def ferment(beans=nil,time=nil)
|
6
|
-
end
|
3
|
+
class Natto
|
4
|
+
def ferment(beans=nil,time=nil)
|
7
5
|
end
|
8
|
-
|
6
|
+
end
|
9
7
|
|
10
|
-
|
8
|
+
module Gimme
|
9
|
+
describe Verifies do
|
11
10
|
|
12
|
-
|
13
|
-
Given { test_double.ferment }
|
14
|
-
When(:result) { lambda { verifier.ferment } }
|
15
|
-
Then { result.should_not raise_error }
|
16
|
-
end
|
11
|
+
Given(:test_double) { gimme(Natto) }
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
shared_examples_for "a verifier" do
|
14
|
+
|
15
|
+
context "invoked once when expected once" do
|
16
|
+
Given { test_double.ferment }
|
17
|
+
When(:result) { lambda { verifier.ferment } }
|
18
|
+
Then { result.should_not raise_error }
|
24
19
|
end
|
25
|
-
end
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
21
|
+
context "never invoked" do
|
22
|
+
When(:result) { lambda { verifier.ferment } }
|
23
|
+
Then { result.should raise_error Errors::VerificationFailedError }
|
24
|
+
Then do result.should raise_error Errors::VerificationFailedError,
|
25
|
+
"expected Natto#ferment to have been called with arguments #{[]}\n"+
|
26
|
+
" but was never called"
|
27
|
+
end
|
33
28
|
end
|
34
|
-
end
|
35
29
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
30
|
+
context "invoked with incorrect args" do
|
31
|
+
Given { test_double.ferment(5) }
|
32
|
+
When(:result) { lambda { verifier.ferment(4) } }
|
33
|
+
Then do result.should raise_error Errors::VerificationFailedError,
|
34
|
+
"expected Natto#ferment to have been called with arguments #{[4]}\n"+
|
35
|
+
" was actually called 1 times with arguments #{[5]}"
|
36
|
+
end
|
43
37
|
end
|
44
|
-
|
45
|
-
|
38
|
+
|
39
|
+
context "invoked incorrectly a whole bunch" do
|
40
|
+
Given { test_double.ferment(5) }
|
41
|
+
Given { test_double.ferment(5) }
|
42
|
+
Given { test_double.ferment(3) }
|
43
|
+
When(:result) { lambda { verifier.ferment(4) } }
|
44
|
+
Then do result.should raise_error Errors::VerificationFailedError,
|
45
|
+
/.* was actually called 2 times with arguments #{Regexp.escape([5].to_s)}.*/m
|
46
|
+
end
|
47
|
+
Then do result.should raise_error Errors::VerificationFailedError,
|
48
|
+
/.* was actually called 1 times with arguments #{Regexp.escape([3].to_s)}.*/m
|
49
|
+
end
|
46
50
|
end
|
47
|
-
end
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
context "invoked too few times" do
|
53
|
+
Given(:verifier) { Verifies.new(test_double,3) }
|
54
|
+
Given { 2.times { test_double.ferment } }
|
55
|
+
When(:result) { lambda { verifier.ferment } }
|
56
|
+
Then { result.should raise_error Errors::VerificationFailedError }
|
57
|
+
end
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
context "juggling multiple verifiers for the same method" do
|
60
|
+
Given(:multi_verifier) { Verifies.new(test_double,2) }
|
61
|
+
Given { test_double.ferment(:panda,:sauce) }
|
62
|
+
Given { 2.times { test_double.ferment(2,3) } }
|
63
|
+
When(:result) do
|
64
|
+
lambda do
|
65
|
+
multi_verifier.ferment(2,3)
|
66
|
+
verifier.ferment(:panda,:sauce)
|
67
|
+
end
|
64
68
|
end
|
69
|
+
Then { result.should_not raise_error }
|
65
70
|
end
|
66
|
-
Then { result.should_not raise_error }
|
67
|
-
end
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
72
|
+
context "a method not on the test_double" do
|
73
|
+
When(:result) { lambda { verifier.eat } }
|
74
|
+
Then { result.should raise_error NoMethodError }
|
75
|
+
end
|
73
76
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
context "a satisfied argument matcher" do
|
78
|
+
Given { test_double.ferment(5) }
|
79
|
+
When(:result) { lambda { verifier.ferment(numeric) } }
|
80
|
+
Then { result.should_not raise_error }
|
81
|
+
end
|
79
82
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
83
|
+
context "an unsatisifed argument matcher" do
|
84
|
+
Given { test_double.ferment("True") }
|
85
|
+
When(:result) { lambda { verifier.ferment(boolean) } }
|
86
|
+
Then { result.should raise_error Errors::VerificationFailedError }
|
87
|
+
end
|
84
88
|
end
|
85
|
-
end
|
86
89
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
shared_examples_for "an overridden verifier" do
|
91
|
+
context "a method not on the double that is invoked" do
|
92
|
+
Given { test_double.eat }
|
93
|
+
When(:result) { lambda { verifier.eat } }
|
94
|
+
Then { result.should_not raise_error }
|
95
|
+
end
|
93
96
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
+
context "a method not on the test_double that is _not_ invoked" do
|
98
|
+
When(:result) { lambda { verifier.eat } }
|
99
|
+
Then { result.should raise_error Errors::VerificationFailedError }
|
100
|
+
end
|
97
101
|
end
|
98
|
-
end
|
99
102
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
103
|
+
context "class API" do
|
104
|
+
Given(:verifier) { Verifies.new(test_double) }
|
105
|
+
it_behaves_like "a verifier"
|
106
|
+
it_behaves_like "an overridden verifier" do
|
107
|
+
Given { verifier.raises_no_method_error = false }
|
108
|
+
end
|
105
109
|
end
|
106
|
-
end
|
107
110
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
111
|
+
context "gimme DSL" do
|
112
|
+
it_behaves_like "a verifier" do
|
113
|
+
Given(:verifier) { verify(test_double) }
|
114
|
+
end
|
112
115
|
|
113
|
-
|
114
|
-
|
116
|
+
it_behaves_like "an overridden verifier" do
|
117
|
+
Given(:verifier) { verify!(test_double) }
|
118
|
+
end
|
115
119
|
end
|
116
120
|
end
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
+
end
|