stendhal 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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