stendhal 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/Readme.md +29 -2
- data/features/mocks.feature +71 -0
- data/lib/stendhal/core_ext/object.rb +3 -0
- data/lib/stendhal/example.rb +2 -0
- data/lib/stendhal/mocks/mock_verifier.rb +71 -0
- data/lib/stendhal/mocks/mockable.rb +34 -0
- data/lib/stendhal/mocks.rb +2 -0
- data/lib/stendhal/version.rb +1 -1
- data/lib/stendhal.rb +1 -0
- data/spec/stendhal/example_spec.rb +20 -0
- data/spec/stendhal/mocks/mock_verifier_spec.rb +138 -0
- data/spec/stendhal/mocks/mockable_spec.rb +26 -0
- data/spec/stendhal/{test_double_spec.rb → mocks/test_double_spec.rb} +0 -0
- metadata +14 -5
data/Gemfile.lock
CHANGED
data/Readme.md
CHANGED
@@ -10,9 +10,9 @@ development.
|
|
10
10
|
|
11
11
|
* Pretty decent reporter with colors
|
12
12
|
* Test doubles and stubs (no partial stubbing yet)
|
13
|
+
* Mocks (message expectations)
|
13
14
|
* Nested example groups (declare them with either describe or context)
|
14
15
|
* Pending examples
|
15
|
-
* Lame reporter (but will get better eventually)
|
16
16
|
* Matchers (use with object.must or object.must_not)
|
17
17
|
|
18
18
|
eq() / eql()
|
@@ -86,6 +86,22 @@ development.
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
describe "Message expectation" do
|
90
|
+
it "is declared with expects" do
|
91
|
+
string = "my string"
|
92
|
+
string.expects(:reverse)
|
93
|
+
|
94
|
+
string.reverse # Expectation fulfilled!
|
95
|
+
end
|
96
|
+
|
97
|
+
it "is declared with does_not_expect in case it is negative" do
|
98
|
+
string = "my string"
|
99
|
+
string.does_not_expect(:reverse)
|
100
|
+
|
101
|
+
string.reverse # Fails!
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
89
105
|
end
|
90
106
|
|
91
107
|
###Running the specs!
|
@@ -114,7 +130,18 @@ development.
|
|
114
130
|
* is declared with double as well
|
115
131
|
* can be given stubs
|
116
132
|
|
117
|
-
|
133
|
+
Message expectation
|
134
|
+
* is declared with expects
|
135
|
+
* is declared with does_not_expect in case it is negative
|
136
|
+
|
137
|
+
13 examples, 3 failures, 2 pending
|
138
|
+
|
139
|
+
##Feedback
|
140
|
+
|
141
|
+
Reporting issues, asking for new features and constructive criticizing can be
|
142
|
+
done either through Github issues, the mailing list
|
143
|
+
(http://librelist.com/browser/stendhal/) or you can always reach me on twitter
|
144
|
+
(I am @txustice). Don't hesitate to tell me anything! :)
|
118
145
|
|
119
146
|
##Note on Patches/Pull Requests
|
120
147
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
Feature: Mocks (message expectations)
|
2
|
+
|
3
|
+
With mocks you can check whether objects receive certain messages.
|
4
|
+
|
5
|
+
Scenario: declare a message expectation
|
6
|
+
Given a directory named "stendhal_project"
|
7
|
+
When I cd to "stendhal_project"
|
8
|
+
Given a file named "sample_spec.rb" with:
|
9
|
+
"""
|
10
|
+
class MyClass
|
11
|
+
|
12
|
+
def foo
|
13
|
+
end
|
14
|
+
|
15
|
+
def bar
|
16
|
+
foo
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "bar" do
|
22
|
+
it "calls foo" do
|
23
|
+
object = MyClass.new
|
24
|
+
object.expects(:foo)
|
25
|
+
|
26
|
+
object.bar
|
27
|
+
end
|
28
|
+
it "never calls foo" do
|
29
|
+
object = MyClass.new
|
30
|
+
object.expects(:foo)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
"""
|
34
|
+
When I run "stendhal sample_spec.rb"
|
35
|
+
Then the exit status should be 0
|
36
|
+
And the output should contain "2 examples, 1 failure"
|
37
|
+
And the output should contain "expected to be sent :foo 1 time, but received it 0 times"
|
38
|
+
|
39
|
+
Scenario: declare a negative message expectation
|
40
|
+
Given a directory named "stendhal_project"
|
41
|
+
When I cd to "stendhal_project"
|
42
|
+
Given a file named "sample_spec.rb" with:
|
43
|
+
"""
|
44
|
+
class MyClass
|
45
|
+
|
46
|
+
def foo
|
47
|
+
end
|
48
|
+
|
49
|
+
def bar
|
50
|
+
foo
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "bar" do
|
56
|
+
it "calls foo" do
|
57
|
+
object = MyClass.new
|
58
|
+
object.does_not_expect(:foo)
|
59
|
+
|
60
|
+
object.bar
|
61
|
+
end
|
62
|
+
it "never calls foo" do
|
63
|
+
object = MyClass.new
|
64
|
+
object.does_not_expect(:foo)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
"""
|
68
|
+
When I run "stendhal sample_spec.rb"
|
69
|
+
Then the exit status should be 0
|
70
|
+
And the output should contain "2 examples, 1 failure"
|
71
|
+
And the output should contain "expected to be sent :foo 0 times, but received it 1 time"
|
data/lib/stendhal/example.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Stendhal
|
2
|
+
module Mocks
|
3
|
+
class MockVerifier
|
4
|
+
|
5
|
+
@@verifiers = []
|
6
|
+
|
7
|
+
attr_reader :expectations
|
8
|
+
|
9
|
+
def initialize(object)
|
10
|
+
@expectations = []
|
11
|
+
@object = object
|
12
|
+
@@verifiers << self
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_expectation(method)
|
16
|
+
@expectations << MessageExpectation.new(method)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_negative_expectation(method)
|
20
|
+
@expectations << MessageExpectation.new(method, :negative => true)
|
21
|
+
end
|
22
|
+
|
23
|
+
def fulfill_expectation(method)
|
24
|
+
@expectations.select{|e| e.method == method}.each(&:register_call)
|
25
|
+
end
|
26
|
+
|
27
|
+
def verify!
|
28
|
+
expectations.each do |expectation|
|
29
|
+
begin
|
30
|
+
expectation.verify
|
31
|
+
rescue Stendhal::Exceptions::ExpectationNotMet=>e
|
32
|
+
raise Stendhal::Exceptions::ExpectationNotMet.new "#{@object} #{e.message}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.verifiers
|
38
|
+
@@verifiers
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.verify!
|
42
|
+
verifiers.each(&:verify!)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.reset!
|
46
|
+
@@verifiers = []
|
47
|
+
end
|
48
|
+
|
49
|
+
class MessageExpectation
|
50
|
+
attr_reader :method
|
51
|
+
attr_reader :times_called
|
52
|
+
attr_reader :times_expected
|
53
|
+
|
54
|
+
def initialize(method, options = {})
|
55
|
+
@method = method
|
56
|
+
@times_expected = options[:negative] ? 0 : 1
|
57
|
+
@times_called = 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def register_call
|
61
|
+
@times_called += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def verify
|
65
|
+
raise Stendhal::Exceptions::ExpectationNotMet.new "expected to be sent :#{method} #{times_expected} time#{times_expected == 1 ? '' : 's'}, but received it #{times_called} time#{times_called == 1 ? '' : 's'}" unless times_expected == times_called
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Stendhal
|
2
|
+
module Mocks
|
3
|
+
module Mockable
|
4
|
+
|
5
|
+
def expects(method, options = {})
|
6
|
+
if options[:negative]
|
7
|
+
__verifier.add_negative_expectation(method)
|
8
|
+
else
|
9
|
+
__verifier.add_expectation(method)
|
10
|
+
end
|
11
|
+
metaclass = (class << self;self;end)
|
12
|
+
metaclass.send(:alias_method, :"__original_#{method}", method.to_sym)
|
13
|
+
metaclass.send(:undef_method, method.to_sym)
|
14
|
+
metaclass.class_eval <<EOT
|
15
|
+
def #{method}(*args, &block)
|
16
|
+
@__verifier.fulfill_expectation(:#{method},*args,&block)
|
17
|
+
__original_#{method}(*args,&block)
|
18
|
+
end
|
19
|
+
EOT
|
20
|
+
end
|
21
|
+
|
22
|
+
def does_not_expect(method)
|
23
|
+
expects(method, :negative => true)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def __verifier
|
29
|
+
@__verifier ||= MockVerifier.new(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/stendhal/mocks.rb
CHANGED
data/lib/stendhal/version.rb
CHANGED
data/lib/stendhal.rb
CHANGED
@@ -60,6 +60,26 @@ module Stendhal
|
|
60
60
|
example.should be_aborted
|
61
61
|
end
|
62
62
|
|
63
|
+
it "verifies all message expectations after running the block" do
|
64
|
+
example = Example.new("docstring") do
|
65
|
+
|
66
|
+
end
|
67
|
+
example.should_receive(:instance_eval).ordered
|
68
|
+
Stendhal::Mocks::MockVerifier.should_receive(:verify!).ordered
|
69
|
+
|
70
|
+
example.run
|
71
|
+
end
|
72
|
+
|
73
|
+
it "resets all message expectations before running the block" do
|
74
|
+
example = Example.new("docstring") do
|
75
|
+
|
76
|
+
end
|
77
|
+
Stendhal::Mocks::MockVerifier.should_receive(:reset!)
|
78
|
+
example.should_receive(:instance_eval).ordered
|
79
|
+
|
80
|
+
example.run
|
81
|
+
end
|
82
|
+
|
63
83
|
end
|
64
84
|
|
65
85
|
describe "#fail" do
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Stendhal
|
4
|
+
module Mocks
|
5
|
+
describe MockVerifier do
|
6
|
+
|
7
|
+
subject { verifier = MockVerifier.new('object') }
|
8
|
+
|
9
|
+
it 'includes itself in verifiers when created' do
|
10
|
+
subject
|
11
|
+
MockVerifier.should have(1).verifiers
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "instance methods" do
|
15
|
+
|
16
|
+
describe "#add_expectation" do
|
17
|
+
it 'adds an expectation for a given method' do
|
18
|
+
MockVerifier::MessageExpectation.should_receive(:new).with(:reverse)
|
19
|
+
|
20
|
+
subject.add_expectation(:reverse)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#add_negative_expectation" do
|
25
|
+
it 'adds a negative expectation for a given method' do
|
26
|
+
MockVerifier::MessageExpectation.should_receive(:new).with(:reverse, :negative => true)
|
27
|
+
|
28
|
+
subject.add_negative_expectation(:reverse)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#fulfill_expectation" do
|
33
|
+
it 'registers a call for a given method' do
|
34
|
+
subject.add_expectation(:reverse)
|
35
|
+
subject.expectations.first.should_receive(:register_call)
|
36
|
+
|
37
|
+
subject.fulfill_expectation(:reverse)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#verify!" do
|
42
|
+
it 'verifies each of the expectations until one fails' do
|
43
|
+
expectations = [double, double, double]
|
44
|
+
expectations.each do |e|
|
45
|
+
e.should_receive(:verify)
|
46
|
+
end
|
47
|
+
subject.should_receive(:expectations).and_return(expectations)
|
48
|
+
|
49
|
+
subject.verify!
|
50
|
+
end
|
51
|
+
it 'adds the object inspect to the message if raised any ExpectationNotMet' do
|
52
|
+
expectations = [double, double, double]
|
53
|
+
expectations.first.should_receive(:verify).and_raise(Stendhal::Exceptions::ExpectationNotMet.new('expected foo to be bar'))
|
54
|
+
|
55
|
+
subject.should_receive(:expectations).and_return(expectations)
|
56
|
+
|
57
|
+
expect {
|
58
|
+
subject.verify!
|
59
|
+
}.to raise_error(Stendhal::Exceptions::ExpectationNotMet, "object expected foo to be bar")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "class methods" do
|
66
|
+
|
67
|
+
describe "#verify!" do
|
68
|
+
it 'verifies each of the verifiers' do
|
69
|
+
verifiers = [double, double, double]
|
70
|
+
verifiers.each { |v| v.should_receive(:verify!) }
|
71
|
+
MockVerifier.should_receive(:verifiers).and_return verifiers
|
72
|
+
|
73
|
+
MockVerifier.verify!
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#reset!" do
|
78
|
+
it 'deletes all verifiers' do
|
79
|
+
MockVerifier.new('object')
|
80
|
+
MockVerifier.new('other object')
|
81
|
+
|
82
|
+
MockVerifier.reset!
|
83
|
+
|
84
|
+
MockVerifier.should have(0).verifiers
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
describe MockVerifier::MessageExpectation do
|
93
|
+
|
94
|
+
subject { MockVerifier::MessageExpectation.new(:length) }
|
95
|
+
|
96
|
+
it "initializes with 0 times called" do
|
97
|
+
subject.times_called.should == 0
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when given no options" do
|
101
|
+
it "initializes with 1 times expected" do
|
102
|
+
subject.times_expected.should == 1
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when given :negative => true" do
|
107
|
+
it "initializes with 0 times expected" do
|
108
|
+
negative_expectation = MockVerifier::MessageExpectation.new(:length, :negative => true)
|
109
|
+
negative_expectation.times_expected.should == 0
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "instance methods" do
|
114
|
+
|
115
|
+
describe "#register_call" do
|
116
|
+
it 'registers a call for the given expectation' do
|
117
|
+
expect{
|
118
|
+
subject.register_call
|
119
|
+
}.to change(subject, :times_called).by(1)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "#verify" do
|
124
|
+
it 'registers a call for the given expectation' do
|
125
|
+
subject.stub(:times_called).and_return 2
|
126
|
+
subject.stub(:times_expected).and_return 1
|
127
|
+
|
128
|
+
expect {
|
129
|
+
subject.verify
|
130
|
+
}.to raise_error(Stendhal::Exceptions::ExpectationNotMet, "expected to be sent :length 1 time, but received it 2 times")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MyArray < Array
|
4
|
+
include Stendhal::Mocks::Mockable
|
5
|
+
end
|
6
|
+
|
7
|
+
module Stendhal
|
8
|
+
module Mocks
|
9
|
+
describe "a Mockable class" do
|
10
|
+
|
11
|
+
subject { MyArray.new([1,2,3]) }
|
12
|
+
|
13
|
+
describe "#expects" do
|
14
|
+
it 'creates a verifier' do
|
15
|
+
subject.expects(:length)
|
16
|
+
subject.send(:__verifier).should be_a(MockVerifier)
|
17
|
+
end
|
18
|
+
it 'creates a message expectation on the given method' do
|
19
|
+
subject.expects(:length)
|
20
|
+
subject.send(:__verifier).expectations.map(&:method).should include(:length)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
File without changes
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 5
|
9
|
+
version: 0.1.5
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Josep M. Bach
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-11-
|
17
|
+
date: 2010-11-09 00:00:00 +01:00
|
18
18
|
default_executable: stendhal
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -91,12 +91,14 @@ files:
|
|
91
91
|
- bin/stendhal
|
92
92
|
- features/examples.feature
|
93
93
|
- features/expectations.feature
|
94
|
+
- features/mocks.feature
|
94
95
|
- features/reporter.feature
|
95
96
|
- features/support/env.rb
|
96
97
|
- features/test_doubles_and_stubs.feature
|
97
98
|
- lib/stendhal.rb
|
98
99
|
- lib/stendhal/autorun.rb
|
99
100
|
- lib/stendhal/core_ext/kernel.rb
|
101
|
+
- lib/stendhal/core_ext/object.rb
|
100
102
|
- lib/stendhal/dsl.rb
|
101
103
|
- lib/stendhal/example.rb
|
102
104
|
- lib/stendhal/example_group.rb
|
@@ -107,6 +109,8 @@ files:
|
|
107
109
|
- lib/stendhal/matchers/kind.rb
|
108
110
|
- lib/stendhal/matchers/predicate.rb
|
109
111
|
- lib/stendhal/mocks.rb
|
112
|
+
- lib/stendhal/mocks/mock_verifier.rb
|
113
|
+
- lib/stendhal/mocks/mockable.rb
|
110
114
|
- lib/stendhal/mocks/test_double.rb
|
111
115
|
- lib/stendhal/reporter.rb
|
112
116
|
- lib/stendhal/version.rb
|
@@ -120,8 +124,10 @@ files:
|
|
120
124
|
- spec/stendhal/matchers/kind_spec.rb
|
121
125
|
- spec/stendhal/matchers/predicate_spec.rb
|
122
126
|
- spec/stendhal/matchers_spec.rb
|
127
|
+
- spec/stendhal/mocks/mock_verifier_spec.rb
|
128
|
+
- spec/stendhal/mocks/mockable_spec.rb
|
129
|
+
- spec/stendhal/mocks/test_double_spec.rb
|
123
130
|
- spec/stendhal/reporter_spec.rb
|
124
|
-
- spec/stendhal/test_double_spec.rb
|
125
131
|
- stendhal.gemspec
|
126
132
|
has_rdoc: true
|
127
133
|
homepage: http://rubygems.org/gems/stendhal
|
@@ -158,6 +164,7 @@ summary: Stendhal is a really simple test framework.
|
|
158
164
|
test_files:
|
159
165
|
- features/examples.feature
|
160
166
|
- features/expectations.feature
|
167
|
+
- features/mocks.feature
|
161
168
|
- features/reporter.feature
|
162
169
|
- features/support/env.rb
|
163
170
|
- features/test_doubles_and_stubs.feature
|
@@ -170,5 +177,7 @@ test_files:
|
|
170
177
|
- spec/stendhal/matchers/kind_spec.rb
|
171
178
|
- spec/stendhal/matchers/predicate_spec.rb
|
172
179
|
- spec/stendhal/matchers_spec.rb
|
180
|
+
- spec/stendhal/mocks/mock_verifier_spec.rb
|
181
|
+
- spec/stendhal/mocks/mockable_spec.rb
|
182
|
+
- spec/stendhal/mocks/test_double_spec.rb
|
173
183
|
- spec/stendhal/reporter_spec.rb
|
174
|
-
- spec/stendhal/test_double_spec.rb
|