stendhal 0.1.5 → 0.1.6

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/.gitignore CHANGED
@@ -3,3 +3,4 @@ pkg/*
3
3
  .bundle
4
4
  coverage
5
5
  tmp/*
6
+ *.rbc
data/Gemfile.lock CHANGED
@@ -1,13 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stendhal (0.1.4)
4
+ stendhal (0.1.6)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
9
  aruba (0.2.2)
10
10
  builder (2.1.2)
11
+ configuration (1.1.0)
11
12
  cucumber (0.8.5)
12
13
  builder (~> 2.1.2)
13
14
  diff-lcs (~> 1.1.2)
@@ -17,7 +18,19 @@ GEM
17
18
  diff-lcs (1.1.2)
18
19
  gherkin (2.1.5)
19
20
  trollop (~> 1.16.2)
21
+ growl (1.0.3)
22
+ guard (0.2.2)
23
+ open_gem (~> 1.4.2)
24
+ thor (~> 0.14.3)
25
+ guard-rspec (0.1.8)
26
+ guard (>= 0.2.0)
20
27
  json_pure (1.4.6)
28
+ launchy (0.3.7)
29
+ configuration (>= 0.0.5)
30
+ rake (>= 0.8.1)
31
+ open_gem (1.4.2)
32
+ launchy (~> 0.3.5)
33
+ rake (0.8.7)
21
34
  rspec (2.0.1)
22
35
  rspec-core (~> 2.0.1)
23
36
  rspec-expectations (~> 2.0.1)
@@ -32,6 +45,7 @@ GEM
32
45
  simplecov-html (>= 0.3.7)
33
46
  simplecov-html (0.3.8)
34
47
  term-ansicolor (1.0.5)
48
+ thor (0.14.4)
35
49
  trollop (1.16.2)
36
50
 
37
51
  PLATFORMS
@@ -40,6 +54,9 @@ PLATFORMS
40
54
  DEPENDENCIES
41
55
  aruba
42
56
  cucumber (= 0.8.5)
57
+ growl
58
+ guard
59
+ guard-rspec
43
60
  rspec
44
61
  simplecov
45
62
  stendhal!
data/Guardfile ADDED
@@ -0,0 +1,15 @@
1
+ # A sample Guardfile
2
+ # More info at http://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2, :bundler => true do
5
+ watch('^spec/(.*)_spec.rb')
6
+ watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('^spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ # watch('^app/(.*)\.rb') { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch('^lib/(.*)\.rb') { |m| "spec/#{m[1]}_spec.rb" }
12
+ watch('^config/routes.rb') { "spec/routing" }
13
+ watch('^app/controllers/application_controller.rb') { "spec/controllers" }
14
+ watch('^spec/factories/(.*)\.rb') { "spec/models" }
15
+ end
data/Readme.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A small test framework developed as a personal kata to improve my ruby.
4
4
 
5
+ Tested with Ruby 1.8.7, 1.9.2, JRuby 1.5.3 and Rubinius 1.1.
6
+
5
7
  Currently under development, there is only basic functionality for now.
6
8
  Below I will be posting whatever features are available throughout the
7
9
  development.
@@ -10,7 +12,7 @@ development.
10
12
 
11
13
  * Pretty decent reporter with colors
12
14
  * Test doubles and stubs (no partial stubbing yet)
13
- * Mocks (message expectations)
15
+ * Mocks (message expectations) with _optionally_ stubbable return values
14
16
  * Nested example groups (declare them with either describe or context)
15
17
  * Pending examples
16
18
  * Matchers (use with object.must or object.must_not)
@@ -94,6 +96,31 @@ development.
94
96
  string.reverse # Expectation fulfilled!
95
97
  end
96
98
 
99
+ it "can be told the number of times it is expected" do
100
+ string = "my string"
101
+ string.expects(:reverse).once # or
102
+ string.expects(:reverse).twice # or
103
+ string.expects(:reverse).exactly(3).times
104
+
105
+ string.reverse # Fails!
106
+ end
107
+
108
+ it "can return a stubbed value" do
109
+ string = "my string"
110
+ string.expects(:reverse).and_returns 'stubbed value'
111
+
112
+ string.reverse # => "stubbed value"
113
+ end
114
+
115
+ it "can return a stubbed proc" do
116
+ string = "my string"
117
+ string.expects(:reverse).and_returns do
118
+ 3 + 4
119
+ end
120
+
121
+ string.reverse # => 7
122
+ end
123
+
97
124
  it "is declared with does_not_expect in case it is negative" do
98
125
  string = "my string"
99
126
  string.does_not_expect(:reverse)
@@ -132,9 +159,12 @@ development.
132
159
 
133
160
  Message expectation
134
161
  * is declared with expects
135
- * is declared with does_not_expect in case it is negative
162
+ * can be told the number of times it is expected [FAILED]
163
+ * can return a stubbed value
164
+ * can return a stubbed proc
165
+ * is declared with does_not_expect in case it is negative [FAILED]
136
166
 
137
- 13 examples, 3 failures, 2 pending
167
+ 16 examples, 4 failures, 2 pending
138
168
 
139
169
  ##Feedback
140
170
 
@@ -10,6 +10,7 @@ Feature: Mocks (message expectations)
10
10
  class MyClass
11
11
 
12
12
  def foo
13
+ 'called foo'
13
14
  end
14
15
 
15
16
  def bar
@@ -20,10 +21,17 @@ Feature: Mocks (message expectations)
20
21
 
21
22
  describe "bar" do
22
23
  it "calls foo" do
24
+ object = MyClass.new
25
+ object.expects(:foo) # Defaults to .once
26
+
27
+ object.bar.must eq('called foo') # The return value is not stubbed
28
+ end
29
+ it "calls foo two times" do
23
30
  object = MyClass.new
24
31
  object.expects(:foo)
32
+ object.expects(:foo) # This makes two calls expected
25
33
 
26
- object.bar
34
+ 2.times { object.bar }
27
35
  end
28
36
  it "never calls foo" do
29
37
  object = MyClass.new
@@ -33,7 +41,7 @@ Feature: Mocks (message expectations)
33
41
  """
34
42
  When I run "stendhal sample_spec.rb"
35
43
  Then the exit status should be 0
36
- And the output should contain "2 examples, 1 failure"
44
+ And the output should contain "3 examples, 1 failure"
37
45
  And the output should contain "expected to be sent :foo 1 time, but received it 0 times"
38
46
 
39
47
  Scenario: declare a negative message expectation
@@ -69,3 +77,75 @@ Feature: Mocks (message expectations)
69
77
  Then the exit status should be 0
70
78
  And the output should contain "2 examples, 1 failure"
71
79
  And the output should contain "expected to be sent :foo 0 times, but received it 1 time"
80
+
81
+ Scenario: declare a message expectation with a number of times
82
+ Given a directory named "stendhal_project"
83
+ When I cd to "stendhal_project"
84
+ Given a file named "sample_spec.rb" with:
85
+ """
86
+ class MyClass
87
+
88
+ def foo
89
+ end
90
+
91
+ def bar
92
+ foo
93
+ end
94
+
95
+ end
96
+
97
+ describe "bar" do
98
+ it "calls foo twice" do
99
+ object = MyClass.new
100
+ object.expects(:foo).twice
101
+
102
+ object.bar
103
+ object.bar
104
+ end
105
+ it "calls foo three times" do
106
+ object = MyClass.new
107
+ object.expects(:foo).exactly(3).times
108
+
109
+ 3.times { object.bar }
110
+ end
111
+ end
112
+ """
113
+ When I run "stendhal sample_spec.rb"
114
+ Then the exit status should be 0
115
+ And the output should contain "2 examples, 0 failures"
116
+
117
+ Scenario: declare a message expectation stubbing the return value
118
+ Given a directory named "stendhal_project"
119
+ When I cd to "stendhal_project"
120
+ Given a file named "sample_spec.rb" with:
121
+ """
122
+ class MyClass
123
+
124
+ def foo
125
+ 'called foo'
126
+ end
127
+
128
+ def bar
129
+ foo
130
+ end
131
+
132
+ end
133
+
134
+ describe "bar" do
135
+ it "calls foo and returns a stubbed value" do
136
+ object = MyClass.new
137
+ object.expects(:foo).and_returns('stubbed foo')
138
+
139
+ object.bar.must eq('stubbed foo')
140
+ end
141
+ it "calls foo three times" do
142
+ object = MyClass.new
143
+ object.expects(:foo).exactly(3).times.and_returns('stubbed foo')
144
+
145
+ 3.times { object.bar.must eq('stubbed foo') }
146
+ end
147
+ end
148
+ """
149
+ When I run "stendhal sample_spec.rb"
150
+ Then the exit status should be 0
151
+ And the output should contain "2 examples, 0 failures"
data/lib/stendhal/dsl.rb CHANGED
@@ -12,7 +12,8 @@ module Stendhal
12
12
  else
13
13
  options = {:pending => true}
14
14
  end
15
- self.add_example Stendhal::Example.new(*args,options,&blk)
15
+ args << options
16
+ self.add_example Stendhal::Example.new(*args, &blk)
16
17
  end
17
18
  end
18
19
 
@@ -5,19 +5,32 @@ module Stendhal
5
5
  @@verifiers = []
6
6
 
7
7
  attr_reader :expectations
8
+ attr_reader :last_mocked_method
9
+ attr_reader :object
8
10
 
9
11
  def initialize(object)
10
12
  @expectations = []
11
13
  @object = object
14
+ @last_mocked_method = nil
12
15
  @@verifiers << self
13
16
  end
14
17
 
15
- def add_expectation(method)
16
- @expectations << MessageExpectation.new(method)
18
+ def expectation_for(method)
19
+ @expectations.detect {|e| e.method == method }
20
+ end
21
+
22
+ def add_expectation(method, options = {})
23
+ @last_mocked_method = method
24
+ expectation_for(method).tap do |expectation|
25
+ expectation and begin
26
+ options[:negative] ? expectation.times_expected = 0 :
27
+ expectation.times_expected += 1
28
+ end
29
+ end || @expectations << MessageExpectation.new(method, options)
17
30
  end
18
31
 
19
32
  def add_negative_expectation(method)
20
- @expectations << MessageExpectation.new(method, :negative => true)
33
+ add_expectation(method, :negative => true)
21
34
  end
22
35
 
23
36
  def fulfill_expectation(method)
@@ -49,7 +62,7 @@ module Stendhal
49
62
  class MessageExpectation
50
63
  attr_reader :method
51
64
  attr_reader :times_called
52
- attr_reader :times_expected
65
+ attr_accessor :times_expected
53
66
 
54
67
  def initialize(method, options = {})
55
68
  @method = method
@@ -8,15 +8,55 @@ module Stendhal
8
8
  else
9
9
  __verifier.add_expectation(method)
10
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
11
+ unless respond_to?(:"__original_#{method}")
12
+ metaclass = (class << self;self;end)
13
+ metaclass.send(:alias_method, :"__original_#{method}", method.to_sym)
14
+ metaclass.send(:undef_method, method.to_sym)
15
+ metaclass.class_eval <<EOT
16
+ def #{method}(*args, &block)
17
+ @__verifier.fulfill_expectation(:#{method},*args,&block)
18
+ __original_#{method}(*args,&block)
19
+ end
19
20
  EOT
21
+ end
22
+ self
23
+ end
24
+
25
+ def once
26
+ raise "This object has no mocks." unless @__verifier
27
+ __verifier.expectation_for(__verifier.last_mocked_method).times_expected = 1
28
+ self
29
+ end
30
+
31
+ def twice
32
+ raise "This object has no mocks." unless @__verifier
33
+ __verifier.expectation_for(__verifier.last_mocked_method).times_expected = 2
34
+ self
35
+ end
36
+
37
+ def exactly(times)
38
+ raise "This object has no mocks." unless @__verifier
39
+ __verifier.expectation_for(__verifier.last_mocked_method).times_expected = times
40
+ self
41
+ end
42
+
43
+ def times
44
+ raise "This object has no mocks." unless @__verifier
45
+ self
46
+ end
47
+
48
+ def and_returns(retval = nil, &block)
49
+ raise "This object has no mocks." unless @__verifier
50
+ method = __verifier.last_mocked_method
51
+ str = retval.to_s
52
+ unless respond_to?(:"__unstubbed_#{method}")
53
+ metaclass = (class << self;self;end)
54
+ metaclass.send(:alias_method, :"__unstubbed_#{method}", :"__original_#{method}")
55
+ metaclass.send(:undef_method, :"__original_#{method}")
56
+
57
+ return_value = block || Proc.new { retval }
58
+ metaclass.send(:define_method, :"__original_#{method}", return_value)
59
+ end
20
60
  end
21
61
 
22
62
  def does_not_expect(method)
@@ -15,7 +15,6 @@ module Stendhal
15
15
  v
16
16
  end
17
17
  end
18
-
19
18
  end
20
19
 
21
20
  end
@@ -1,3 +1,3 @@
1
1
  module Stendhal
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
@@ -12,18 +12,52 @@ module Stendhal
12
12
  end
13
13
 
14
14
  describe "instance methods" do
15
-
15
+
16
+ describe "#expectation_for(method)" do
17
+ it 'returns the expectation for a given method' do
18
+ subject.add_expectation(:length)
19
+ subject.add_expectation(:class)
20
+
21
+ subject.expectation_for(:length).should be_a(MockVerifier::MessageExpectation)
22
+ subject.expectation_for(:length).method.should == :length
23
+ end
24
+ end
25
+
16
26
  describe "#add_expectation" do
17
27
  it 'adds an expectation for a given method' do
18
- MockVerifier::MessageExpectation.should_receive(:new).with(:reverse)
28
+ MockVerifier::MessageExpectation.should_receive(:new).with(:reverse, {})
19
29
 
20
30
  subject.add_expectation(:reverse)
21
31
  end
32
+ it 'remembers last mocked method' do
33
+ subject.add_expectation(:reverse)
34
+ subject.last_mocked_method.should == :reverse
35
+ end
36
+ context "if an expectation for such method already exists" do
37
+ it 'adds an expected call to that expectation' do
38
+ subject.add_expectation(:reverse)
39
+ MockVerifier::MessageExpectation.should_not_receive(:new)
40
+
41
+ subject.add_expectation(:reverse)
42
+
43
+ subject.should have(1).expectations
44
+ subject.expectations.first.times_expected.should == 2
45
+ end
46
+ end
22
47
  end
23
48
 
24
49
  describe "#add_negative_expectation" do
25
50
  it 'adds a negative expectation for a given method' do
26
- MockVerifier::MessageExpectation.should_receive(:new).with(:reverse, :negative => true)
51
+ subject.add_expectation(:reverse)
52
+ subject.add_expectation(:reverse)
53
+
54
+ subject.add_negative_expectation(:reverse)
55
+
56
+ subject.should have(1).expectations
57
+ subject.expectations.first.times_expected.should == 0
58
+ end
59
+ it 'overrides previous positive expectations on the same method' do
60
+ subject.should_receive(:add_expectation).with(:reverse, :negative => true)
27
61
 
28
62
  subject.add_negative_expectation(:reverse)
29
63
  end
@@ -19,8 +19,111 @@ module Stendhal
19
19
  subject.expects(:length)
20
20
  subject.send(:__verifier).expectations.map(&:method).should include(:length)
21
21
  end
22
+ it 'saves the original method' do
23
+ subject.expects(:length)
24
+ subject.should respond_to(:__original_length)
25
+ subject.send(:__original_length).should == 3
26
+ end
27
+ it 'makes the mocked method register a call and call the original method afterwards' do
28
+ subject.expects(:length)
29
+ subject.send(:__verifier).should_receive(:fulfill_expectation).once.ordered
30
+ subject.should_receive(:__original_length).once.ordered
31
+
32
+ subject.length
33
+ end
34
+ context "when the method is already mocked" do
35
+ it 'does not redefine it' do
36
+ subject.stub(:__original_length)
37
+ metaclass = (class << subject; self; end)
38
+ metaclass.should_not_receive(:alias_method)
39
+ metaclass.should_not_receive(:undef_method)
40
+ metaclass.should_not_receive(:class_eval)
41
+
42
+ subject.expects(:length)
43
+ end
44
+ end
45
+ it 'returns itself to allow chaining' do
46
+ subject.expects(:length).should be_a(MyArray)
47
+ end
22
48
  end
23
49
 
50
+ describe "times a method is expected to be received" do
51
+
52
+ {:once => 1,
53
+ :twice => 2}.each do |name,times|
54
+
55
+ describe "##{times}" do
56
+ it 'sets the times expected for the mocked method to one' do
57
+ expectation = double('expectation', :times_expected => 7)
58
+ subject.send(:__verifier).should_receive(:last_mocked_method).and_return :length
59
+ subject.send(:__verifier).should_receive(:expectation_for).with(:length).and_return expectation
60
+ expectation.should_receive(:times_expected=).with(times)
61
+
62
+ subject.send(name)
63
+ end
64
+ it 'returns itself to allow chaining' do
65
+ subject.send(:__verifier).stub(:last_mocked_method).and_return :length
66
+ subject.expects(:length).send(name).should be_a(MyArray)
67
+ end
68
+ it 'raises an error if called without a previous mock' do
69
+ expect {
70
+ subject.send(name)
71
+ }.to raise_error("This object has no mocks.")
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ describe "#exactly(number_of_times)" do
78
+ it 'sets the times expected for the mocked method to the given number' do
79
+ expectation = double('expectation', :times_expected => 7)
80
+ subject.send(:__verifier).should_receive(:last_mocked_method).and_return :length
81
+ subject.send(:__verifier).should_receive(:expectation_for).with(:length).and_return expectation
82
+ expectation.should_receive(:times_expected=).with(3)
83
+
84
+ subject.exactly(3)
85
+ end
86
+ it 'returns itself to allow chaining' do
87
+ subject.send(:__verifier).stub(:last_mocked_method).and_return :length
88
+ subject.expects(:length).twice.should be_a(MyArray)
89
+ end
90
+ it 'raises an error if called without a previous mock' do
91
+ expect {
92
+ subject.exactly(2)
93
+ }.to raise_error("This object has no mocks.")
94
+ end
95
+ end
96
+
97
+ describe "#times" do
98
+ it 'returns itself to allow chaining' do
99
+ subject.expects(:length).times.should be_a(MyArray)
100
+ end
101
+ it 'raises an error if called without a previous mock' do
102
+ expect {
103
+ subject.times
104
+ }.to raise_error("This object has no mocks.")
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ describe "#and_returns" do
111
+ it 'stubs the return value' do
112
+ subject.expects(:length).and_returns 5
113
+ subject.length.should == 5
114
+ end
115
+ it 'accepts a block as the return value' do
116
+ subject.expects(:length).and_returns do
117
+ 3 + 4
118
+ end
119
+ subject.length.should == 7
120
+ end
121
+ it 'saves the unstubbed method' do
122
+ subject.expects(:length).and_returns(:foo)
123
+ subject.should respond_to(:__unstubbed_length)
124
+ subject.send(:__unstubbed_length).should == 3
125
+ end
126
+ end
24
127
  end
25
128
  end
26
129
  end
data/stendhal.gemspec CHANGED
@@ -20,6 +20,10 @@ Gem::Specification.new do |s|
20
20
  s.add_development_dependency "cucumber", "=0.8.5"
21
21
  s.add_development_dependency "aruba"
22
22
 
23
+ s.add_development_dependency "guard"
24
+ s.add_development_dependency "guard-rspec"
25
+ s.add_development_dependency "growl"
26
+
23
27
  s.files = `git ls-files`.split("\n")
24
28
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
29
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 5
9
- version: 0.1.5
8
+ - 6
9
+ version: 0.1.6
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-09 00:00:00 +01:00
17
+ date: 2010-11-13 00:00:00 +01:00
18
18
  default_executable: stendhal
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -71,6 +71,45 @@ dependencies:
71
71
  version: "0"
72
72
  type: :development
73
73
  version_requirements: *id004
74
+ - !ruby/object:Gem::Dependency
75
+ name: guard
76
+ prerelease: false
77
+ requirement: &id005 !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ type: :development
86
+ version_requirements: *id005
87
+ - !ruby/object:Gem::Dependency
88
+ name: guard-rspec
89
+ prerelease: false
90
+ requirement: &id006 !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ type: :development
99
+ version_requirements: *id006
100
+ - !ruby/object:Gem::Dependency
101
+ name: growl
102
+ prerelease: false
103
+ requirement: &id007 !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ type: :development
112
+ version_requirements: *id007
74
113
  description: Stendhal is a really simple test framework.
75
114
  email:
76
115
  - josep.m.bach@gmail.com
@@ -86,6 +125,7 @@ files:
86
125
  - .rvmrc
87
126
  - Gemfile
88
127
  - Gemfile.lock
128
+ - Guardfile
89
129
  - Rakefile
90
130
  - Readme.md
91
131
  - bin/stendhal