rspec 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -13,6 +13,15 @@
13
13
  * Make sure the PKG_VERSION constant in Rakefile.rb is
14
14
  consistent with the latest version in this document.
15
15
 
16
+ == Version 0.3.0
17
+
18
+ It's been a while since last release, lots of new stuff is available. For instance:
19
+
20
+ * improvements to the runners
21
+ * addition of should_raise expectation (thanks to Brian Takita)
22
+ * some documentation improvements
23
+ * RSpec usable as a DSL
24
+
16
25
  == Version 0.2.0
17
26
 
18
27
  This release provides a tutorial for new users wishing to get started with
data/README CHANGED
@@ -1,45 +1,38 @@
1
1
  = RSpec - Behaviour Specification Framework for Ruby
2
2
 
3
- RSpec is a behaviour specification framework for Ruby. RSpec was created in
4
- response to Dave Astels' article _A New Look at Test Driven Development_ which
5
- can be read at: http://daveastels.com/index.php?p=5 RSpec is intended to
6
- provide the features discussed in Dave's article.
3
+ RSpec is a behaviour specification framework for Ruby. RSpec was created in response to Dave Astels' article _A New Look at Test Driven Development_ which can be read at: http://daveastels.com/index.php?p=5 RSpec is intended to provide the features discussed in Dave's article.
7
4
 
8
- RSpec was first explored by Steven Baker <srbaker@pobox.com> and Gabriel
9
- Bauman <gbauman@gmail.com>, and has since received numerous contributions
10
- from Dave himself.
5
+ RSpec is primarily developed by Steven Baker <srbaker@pobox.com>, and was inspired by and contributed to by Dave Astels. Special thanks also to Aslak Hellesoy for many significant contributions.
6
+
7
+ RSpec's included mocking framework is based on SchMock by Ben Griffiths. SchMock can be found at http://rubyforge.org/projects/schmock/.
11
8
 
12
9
  == Download/Installation
13
10
 
14
11
  === Using RubyGems
15
12
 
16
- sudo gem install rspec
13
+ $ sudo gem install rspec
17
14
 
18
15
  === Getting the latest sources
19
16
 
20
- RSpec is currently being hosted in the Monotone distributed version control system. To checkout RSpec, please download and install Monotone according to the documentation on the monotone website. Then use the following set of commands to grab the latest RSpec sources.
17
+ RSpec was previously hosted under the Monotone distributed version control system. Due to the increasing popularity of RSpec, and the fact that a large part of the world hasn't yet discovered how wonderful Monotone is, we have succumbed to peer pressure and moved the repository to Subversion.
21
18
 
22
- * monotone --db=database db init
23
- * monotone --db=database pull goliath.fundynet.ca org.rubyforge.rspec
24
- * monotone --db=database --branch=org.rubyforge.rspec checkout RSpec
19
+ RSpec's repository is located at svn://svn.behaviourdriven.org/rspec
25
20
 
26
- The gem can be created and installed from sources as follows:
21
+ == Creating a Gem
27
22
 
28
- rake gem; sudo gem install pkg/rspec_x.y.z.gem
23
+ If you have checked out the sources from version control, you can create a Gem from the checked out version with the 'gem' rake task, like so:
29
24
 
30
- Please visit this page periodically, it will be updated with more information as it is made available including documentation, mailing lists, bug tracking, and more. Special thanks to RubyForge for hosting this project site.
25
+ $ rake gem
31
26
 
32
- == Usage
27
+ This will create a gem in the pkg/ subdirectory. To install the created gem, simply run 'gem install' as root (preferably using sudo(1)):
33
28
 
34
- This will also install a new executable for you, 'spec'. Now you
29
+ $ sudo gem install pkg/rspec_x.y.z.gem
35
30
 
36
- Do a fun dance.
31
+ Please visit the RSpec web site periodically, it will be updated with more information as it is made available including documentation, mailing lists, bug tracking, and more. Special thanks to RubyForge for hosting the project site.
37
32
 
38
- spec movie_spec.rb
33
+ == Usage
39
34
 
40
- Do another dance.
35
+ This will also install a new executable for you, 'spec'. Now you can
41
36
 
37
+ spec movie_spec.rb
42
38
  spec *_spec.rb
43
-
44
- This is the coolest part. Dance harder. Now drink.
45
-
@@ -19,7 +19,7 @@ PKG_NAME = "rspec"
19
19
  # (This is subject to change - AH)
20
20
  #
21
21
  # REMEMBER TO KEEP PKG_VERSION IN SYNC WITH CHANGELOG
22
- PKG_VERSION = "0.2.0"
22
+ PKG_VERSION = "0.3.0"
23
23
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
24
24
  PKG_FILES = FileList[
25
25
  '[A-Z]*',
data/TUTORIAL CHANGED
@@ -40,12 +40,13 @@ ready for use!
40
40
  == Expectations
41
41
 
42
42
  The simplest part of your set of specifications will be an expectation. An
43
- expectation tells RSpec what the expected result of a piece code is. Your set of
44
- specifications is simply a collection of expectations about how your code should
45
- behave.
43
+ expectation tells RSpec what the expected result of a piece code is. Your set
44
+ of specifications is simply a collection of expectations about how your code
45
+ should behave.
46
46
 
47
- In RSpec, the expectations are methods that are available on every object in the
48
- system. If I want to set an expectation that the value of 1 is 1, I would write:
47
+ In RSpec, the expectations are methods that are available on every object in
48
+ the system. If I want to set an expectation that the value of 1 is 1, I would
49
+ write:
49
50
 
50
51
  1.should_equal 1
51
52
 
@@ -63,8 +64,8 @@ not equal 2, write the following:
63
64
 
64
65
  These examples are extremely simple, but they accurately convey the simplicity
65
66
  with which expectations can be written. When specifying your software, you are
66
- simply writing expectations that specify how your software will behave. The rest
67
- of the constructs in RSpec exist entirely to help you organize your
67
+ simply writing expectations that specify how your software will behave. The
68
+ rest of the constructs in RSpec exist entirely to help you organize your
68
69
  expectations, and to provide accurate and helpful reporting when these
69
70
  expectations are not met.
70
71
 
@@ -72,11 +73,11 @@ Eventually, writing expectations for addition and subtraction of numbers will
72
73
  get boring. When that happens, you can start writing expectations for your own
73
74
  code. Let's try a slightly more involved example.
74
75
 
75
- We have been asked to write a robot which will start at a given X, Y coordinate,
76
- and will move in for directions a given number of units. If no starting
77
- coordinate is given, the robot will start at 0,0. When the robot is moved, a
78
- two-element array should be returned with the x and y coordinates. Some
79
- expectations you might write for this robot might be the following:
76
+ We have been asked to write a robot which will start at a given X, Y
77
+ coordinate, and will move in for directions a given number of units. If no
78
+ starting coordinate is given, the robot will start at 0,0. When the robot is
79
+ moved, a two-element array should be returned with the x and y coordinates.
80
+ Some expectations you might write for this robot might be the following:
80
81
 
81
82
  rob = Robot.new
82
83
 
@@ -94,9 +95,9 @@ expectations you might write for this robot might be the following:
94
95
  The code example above isn't intended to demonstrate good design, but has been
95
96
  created explicitly to demonstrate the use of the expectation methods.
96
97
 
97
- Also note that these expectations would be written iteratively, but the purpose
98
- of this document is to serve as an introduction to the RSpec framework, not to
99
- Behaviour Driven Development itself.
98
+ Also note that these expectations would be written iteratively, but the
99
+ purpose of this document is to serve as an introduction to the RSpec
100
+ framework, not to Behaviour Driven Development itself.
100
101
 
101
102
  As you can see, writing expectations can be rather verbose, even for a simple
102
103
  project like the example above. For clarity, your expectations will be grouped
@@ -104,11 +105,12 @@ into specifications, which are described in the next section.
104
105
 
105
106
  == Specifications
106
107
 
107
- Your software will be specified by writing expectations, which were explained in
108
- the previous section. Your expectations will be grouped into specifications for
109
- clarity. A specification is simply a method that contains expectations. The name
110
- of your specification will be used by the Spec Runner (see Running and
111
- Reporting) to inform you of unmet expectations, so choose your names wisely.
108
+ Your software will be specified by writing expectations, which were explained
109
+ in the previous section. Your expectations will be grouped into specifications
110
+ for clarity. A specification is simply a method that contains expectations.
111
+ The name of your specification will be used by the Spec Runner (see Running
112
+ and Reporting) to inform you of unmet expectations, so choose your names
113
+ wisely.
112
114
 
113
115
  Consider the robot example from the previous example, but divided into
114
116
  specification methods:
@@ -137,40 +139,37 @@ specification methods:
137
139
  rob.move_west(15).should_equal [-5, 5]
138
140
  end
139
141
 
140
- By dividing expectations into specification methods, our specifications for the
141
- behaviour of our software are far easier to read and clearly express the intent.
142
- Even those who are unfamiliar with Ruby specifically (and perhaps even some who
143
- are relatively unfamiliar with software development in general) can easily read
144
- our specifications.
142
+ By dividing expectations into specification methods, our specifications for
143
+ the behaviour of our software are far easier to read and clearly express the
144
+ intent. Even those who are unfamiliar with Ruby specifically (and perhaps even
145
+ some who are relatively unfamiliar with software development in general) can
146
+ easily read our specifications.
145
147
 
146
- == Fixtures
148
+ == Contexts
147
149
 
148
150
  As you can see, there is some duplicated code in the example from the last
149
151
  section in the creation of the Robot objects. You will find that the
150
- initialization of objects will often be duplicated when writing specifications.
152
+ initialization of objects will often be duplicated when writing
153
+ specifications.
154
+
151
155
  RSpec provides you with a way of setting up this data ahead of time, by
152
156
  providing two methods: setup and teardown.
153
157
 
154
158
  The setup method is called before invoking each specification to allow you to
155
159
  set up resources which are required for each specification, and the teardown
156
160
  method is called after invoking each specification, to allow you to
157
- appropriately deallocate or close resources wich were required for the
161
+ appropriately deallocate or close resources which were required for the
158
162
  specifications.
159
163
 
160
- The example in the next section include a demonstration of the usage of fixtures
161
- in Contexts.
162
-
163
- == Contexts
164
-
165
164
  The specifications described in the Specifications example are absolutely
166
- useless if they're not placed in a context. Because you will be writing a large
167
- number of specifications for your software, you will want a way to divide them
168
- into logical groups. Specification methods are grouped within subclasses of
169
- Context. You can define as many contexts as you wish, and you are encouraged to
170
- divide your software into many well-defined contexts.
165
+ useless if they're not placed in a context. Because you will be writing a
166
+ large number of specifications for your software, you will want a way to
167
+ divide them into logical groups. Specification methods are grouped within
168
+ subclasses of Context. You can define as many contexts as you wish, and you
169
+ are encouraged to divide your software into many well-defined contexts.
171
170
 
172
- The following Context is a complete specification for the movement of the robot,
173
- and if it were run, would produce useful output.
171
+ The following Context is a complete specification for the movement of the
172
+ robot, and if it were run, would produce useful output.
174
173
 
175
174
  require 'spec'
176
175
  class RobotMovement < Spec::Context
@@ -193,16 +192,16 @@ and if it were run, would produce useful output.
193
192
  === Specification Naming
194
193
 
195
194
  RSpec does not require the use of a specific naming scheme for specifications.
196
- When a context is run, all of the public methods which you define in the context
197
- will be executed. The only exceptions are currently methods which are used
198
- internally by RSpec. The forbidden names are initialize, mock, violated, and
199
- run. Because of Ruby's dynamic nature, you will not see a warning if you
195
+ When a context is run, all of the public methods which you define in the
196
+ context will be executed. The only exceptions are currently methods which are
197
+ used internally by RSpec. The forbidden names are initialize, mock, violated,
198
+ and run. Because of Ruby's dynamic nature, you will not see a warning if you
200
199
  override any of these methods, and unexpected behaviour may ensue.
201
200
 
202
201
  In addition to the four aforementioned forbidden specification method names,
203
- methods named with a leading underscore will also not be run as specifications.
204
- This allows you a way to define methods for your own use within the Context and
205
- not have them run as specifications.
202
+ methods named with a leading underscore will also not be run as
203
+ specifications. This allows you a way to define methods for your own use
204
+ within the Context and not have them run as specifications.
206
205
 
207
206
  == Running and Reporting
208
207
 
@@ -223,10 +222,11 @@ examples, you will get the following output:
223
222
 
224
223
  The spec runner counts the specification as it executes them by printing a
225
224
  single dot (.) to the screen. It indicates a failure with an X. For every
226
- failure, the specification runner will provide a backtrace indicating where the
227
- failure occured. A failure is basically a raised exception. Either an exception
228
- raised by the software you're specifying, by Ruby itself (such as SyntaxError or
229
- RuntimeError), or it will be an unmet expectation (ExpectationNotMet).
225
+ failure, the specification runner will provide a backtrace indicating where
226
+ the failure occurred. A failure is basically a raised exception. Either an
227
+ exception raised by the software you're specifying, by Ruby itself (such as
228
+ SyntaxError or RuntimeError), or it will be an unmet expectation
229
+ (ExpectationNotMet).
230
230
 
231
231
  For instance, if you expect "Space Balls" to be in OneMovieList which actually
232
232
  includes "Star Wars", you will get the following error:
@@ -244,8 +244,8 @@ includes "Star Wars", you will get the following error:
244
244
 
245
245
  4 specifications, 4 expectations, 1 failures
246
246
 
247
- This informs you that the MovieList object should include "Space Balls", but got
248
- an ExpectationNotMet error. The backtrace also informs you that the unmet
247
+ This informs you that the MovieList object should include "Space Balls", but
248
+ got an ExpectationNotMet error. The backtrace also informs you that the unmet
249
249
  expectation can be found in the file 'movie_spec.rb' on line 34, in the
250
250
  `should_include_space_balls' method.
251
251
 
@@ -253,7 +253,7 @@ expectation can be found in the file 'movie_spec.rb' on line 34, in the
253
253
 
254
254
  This document was by no means intended to provide an exhaustive overview of
255
255
  Behaviour Driven Development in general, or RSpec specifically. It is intended
256
- as a tutorial to get you started with specifying the behaviour of your software
257
- with RSpec. More information can be found in the API documentation of RSpec
258
- itself. Further documentation on BDD, and RSpec is on the way.
256
+ as a tutorial to get you started with specifying the behaviour of your
257
+ software with RSpec. More information can be found in the API documentation of
258
+ RSpec itself. Further documentation on BDD, and RSpec is on the way.
259
259
 
@@ -0,0 +1,15 @@
1
+ require 'spec'
2
+
3
+ class AddSpecification < Spec::Context
4
+
5
+ def a_passing_spec
6
+ true.should_equal true
7
+ end
8
+
9
+ def a_failing_spec
10
+ true.should_equal false
11
+ end
12
+
13
+ end
14
+
15
+ AddSpecification.add_specification('another_failing_spec') { false.should_equal true }
@@ -0,0 +1,8 @@
1
+ specification "failing_spec" do
2
+ true.should_equal false
3
+ end
4
+
5
+
6
+ specification "passing_spec" do
7
+ true.should_equal true
8
+ end
@@ -2,5 +2,7 @@ require 'spec/context'
2
2
  require 'spec/expectations'
3
3
  require 'spec/exceptions'
4
4
  require 'spec/text_runner'
5
+ require 'spec/gui_runner'
5
6
  require 'spec/mock'
6
7
  require 'spec/collector'
8
+ require 'spec/dsl'
@@ -50,13 +50,21 @@ module Spec
50
50
  rescue Exception
51
51
  result_listener.failure(@specification, $!)
52
52
  ensure
53
- teardown
54
- verify_mocks
53
+ begin
54
+ teardown
55
+ verify_mocks
56
+ rescue Exception
57
+ result_listener.failure(@specification, $!)
58
+ end
55
59
  end
56
60
 
57
61
  return result
58
62
  end
59
63
 
64
+ def self.add_specification(name, &block)
65
+ self.send(:define_method, name.to_sym, Proc.new { || block.call })
66
+ end
67
+
60
68
  private
61
69
 
62
70
  def self.my_methods
@@ -0,0 +1,23 @@
1
+ require 'spec'
2
+
3
+ module DSLExtensions
4
+
5
+ def context(name)
6
+ eval "$#{name} = Class.new(Spec::Context)"
7
+ $current_context = eval "$#{name}"
8
+ end
9
+
10
+ def specification(name, &block)
11
+ $default_context ||= Class.new(Spec::Context)
12
+ $current_context = $default_context if $current_context.nil?
13
+
14
+ $current_context.add_specification(name.to_sym, &block)
15
+ end
16
+
17
+ end
18
+
19
+ class Object
20
+ include DSLExtensions
21
+ end
22
+
23
+ alias example specification if ENV['USER'] == 'marick'
@@ -1,6 +1,5 @@
1
1
  module Spec
2
- module Expectations
3
-
2
+ module ExpectationHelperMethods
4
3
  def should(message=nil)
5
4
  message ||= "Expectation not met."
6
5
  if (! yield)
@@ -11,6 +10,10 @@ module Spec
11
10
  def default_message(expectation, expected)
12
11
  "<#{self.inspect}:#{self.class}>\n#{expectation}:\n<#{expected.inspect}:#{expected.class}>"
13
12
  end
13
+ end
14
+
15
+ module ObjectExpectations
16
+ include ExpectationHelperMethods
14
17
 
15
18
  def should_equal(expected, message=nil)
16
19
  message ||= default_message("should be equal to", expected)
@@ -81,11 +84,50 @@ module Spec
81
84
  message ||= "<#{self}> should be false"
82
85
  should(message) { not self }
83
86
  end
87
+
84
88
  end
85
-
89
+
90
+
91
+ module ProcExpectations
92
+ include ExpectationHelperMethods
93
+
94
+ def should_raise(exception=Exception, message=nil)
95
+ message ||= default_message("should raise", exception.class.to_s)
96
+ should(message) do
97
+ begin
98
+ self.call
99
+ false
100
+ rescue exception
101
+ true
102
+ rescue
103
+ false
104
+ end
105
+ end
106
+ end
107
+
108
+ def should_not_raise(exception=Exception, message=nil)
109
+ message ||= default_message("should not raise", exception.class.to_s)
110
+ should(message) do
111
+ begin
112
+ self.call
113
+ true
114
+ rescue exception
115
+ false
116
+ rescue
117
+ true
118
+ end
119
+ end
120
+ end
121
+
122
+ end
123
+
86
124
  end
87
125
 
88
126
 
89
127
  class Object
90
- include Spec::Expectations
128
+ include Spec::ObjectExpectations
129
+ end
130
+
131
+ class Proc
132
+ include Spec::ProcExpectations
91
133
  end
@@ -0,0 +1,59 @@
1
+ require 'socket'
2
+ @socket
3
+ module Spec
4
+ class GuiRunner
5
+
6
+ def initialize(port)
7
+ @failures = Array.new
8
+ @specification_count = 0
9
+ @failure_count = 0
10
+ @failed = false
11
+ @socket = TCPSocket.new("127.0.0.1", port)
12
+ @socket << "connected\n"
13
+ end
14
+
15
+ def run(context_or_collection = Spec::Collector)
16
+ specs = context_or_collection.collection
17
+ start_run specs.size
18
+ specs.each {|spec| spec.run(self)}
19
+ end_run
20
+ @socket.shutdown
21
+ end
22
+
23
+ def spec(spec)
24
+ @failed = false
25
+ end
26
+
27
+ def pass(spec)
28
+ @socket << "passed\n"
29
+ end
30
+
31
+ def failure(spec, exception)
32
+ return if @failed
33
+ @socket << "failed\n"
34
+ @failed = true
35
+ dump_failure(exception)
36
+ end
37
+
38
+ def start_run(number_of_specs)
39
+ @socket << "start #{number_of_specs.to_i}\n"
40
+ end
41
+
42
+ def end_run
43
+ @socket << "end\n"
44
+ end
45
+
46
+ def dump_failure(exception)
47
+ @socket << "#{exception.message} (#{exception.class.name})\n"
48
+ dump_backtrace(exception.backtrace)
49
+ @socket << "!\n"
50
+ end
51
+
52
+ def dump_backtrace(trace)
53
+ lines = trace.reject {|line| line.include? "lib/spec"}.reject {|line | line.include? "./spec:"}
54
+ @socket << lines.join("\n")
55
+ @socket << "\n"
56
+ end
57
+
58
+ end
59
+ end
@@ -39,7 +39,7 @@ class Mock
39
39
  # act as null object if method is missing and we ignore them. return value too!
40
40
  @options[:null_object] ? self : super(sym, *args, &block)
41
41
  rescue NoMethodError
42
- raise Spec::Exceptions::MockExpectationError, "Mock '#{@name}' received unexpected message '#{sym.to_s}'"
42
+ raise Spec::Exceptions::MockExpectationError, "Mock '#{@name}' received unexpected message '#{sym.to_s}' with " + (args.collect{|arg| "<#{arg}:#{arg.class.name}>"}.join(", "))
43
43
  end
44
44
  end
45
45
  end
@@ -47,7 +47,7 @@ class Mock
47
47
  private
48
48
 
49
49
  def find_matching_expectation(sym, *args)
50
- expectation = @expectations.find {|expectation| expectation.matches(sym, *args)}
50
+ expectation = @expectations.find {|expectation| expectation.matches(sym, args)}
51
51
  expectation
52
52
  end
53
53
 
@@ -65,28 +65,38 @@ class MessageExpectation
65
65
  @received_count = 0
66
66
  @expected_received_count = 1
67
67
  @expected_params = nil
68
+ @consecutive = false
68
69
  end
69
70
 
70
- def matches(sym, *args)
71
- @sym == sym
71
+ def matches(sym, args)
72
+ @sym == sym and (@expected_params.nil? or @expected_params == args)
72
73
  end
73
74
 
74
75
  # This method is called at the end of a spec, after teardown.
75
76
  def verify_messages_received
76
77
  # TODO: this doesn't provide good enough error messages to fix the error.
77
78
  # Error msg should tell exactly what went wrong. (AH).
78
- unless @expected_received_count == @received_count
79
- expected_signature = nil
80
- if @expected_params.nil?
81
- expected_signature = @sym
82
- else
83
- params = @expected_params.collect{|param| "<#{param}:#{param.class.name}>"}.join(", ")
84
- expected_signature = "#{@sym}(#{params})"
85
- end
86
- message = "#{@expected_from}: Mock '#{@mock_name}' expected #{expected_signature} #{@expected_received_count} times, but received it #{@received_count} times"
79
+
80
+ return if @expected_received_count == -2
81
+ return if (@expected_received_count == -1) && (@received_count > 0)
82
+ return if @expected_received_count == @received_count
83
+
84
+ expected_signature = nil
85
+ if @expected_params.nil?
86
+ expected_signature = @sym
87
+ else
88
+ params = @expected_params.collect{|param| "<#{param}:#{param.class.name}>"}.join(", ")
89
+ expected_signature = "#{@sym}(#{params})"
90
+ end
91
+
92
+ count_message = "{@expected_received_count} times"
93
+ count_message = "at least once" if (@expected_received_count == -1)
94
+ count_message = "never" if (@expected_received_count == 0)
95
+ count_message = "once" if (@expected_received_count == 1)
96
+ count_message = "twice" if (@expected_received_count == 2)
87
97
 
98
+ message = "#{@expected_from}: Mock '#{@mock_name}' expected #{expected_signature} #{count_message}, but received it #{@received_count} times"
88
99
  raise Spec::Exceptions::MockExpectationError, message
89
- end
90
100
  end
91
101
 
92
102
  # This method is called when a method is invoked on a mock
@@ -103,11 +113,15 @@ class MessageExpectation
103
113
 
104
114
  unless @expected_params.nil? or @expected_params == args
105
115
  raise Spec::Exceptions::MockExpectationError,
106
- "#{@sym}: Parameter mismatch: Expected <#{@expected_params}>, got <#{@args}>"
116
+ "#{@sym}: Parameter mismatch: Expected <#{@expected_params}>, got <#{args}>"
107
117
  end
108
118
  args << block unless block.nil?
109
119
  @received_count += 1
110
- @block.call(*args)
120
+ value = @block.call(*args)
121
+
122
+ return value unless @consecutive
123
+
124
+ value[[@received_count, value.size].min - 1]
111
125
  end
112
126
 
113
127
  def with(*args)
@@ -126,8 +140,14 @@ class MessageExpectation
126
140
  end
127
141
 
128
142
  def at_least_once
143
+ @expected_received_count = -1
129
144
  self
130
145
  end
146
+
147
+ def any_number_of_times
148
+ @expected_received_count = -2
149
+ self
150
+ end
131
151
 
132
152
  def never
133
153
  @expected_received_count = 0
@@ -147,9 +167,16 @@ class MessageExpectation
147
167
  def returns(value=nil,&block)
148
168
  @block = block_given? ? block : proc { value }
149
169
  end
170
+
171
+ def returns_consecutively(value=[nil],&block)
172
+ @consecutive = true
173
+ @block = block_given? ? block : proc { value }
174
+ end
175
+
150
176
  # this reads better in English IMHO: (AH)
151
177
  # uri_specs.should_receive(:[]).with(:overview).and_return("http://some.host/look_here/\#{path}")
152
178
  alias :and_return :returns
179
+ alias :and_return_consecutively :returns_consecutively
153
180
 
154
181
  end
155
182
 
@@ -4,8 +4,8 @@ module Spec
4
4
  def initialize(appendable = $stdout)
5
5
  @failures = Array.new
6
6
  @specification_count = 0
7
- @expectation_count = 0
8
7
  @failure_count = 0
8
+ @failed = false
9
9
  @output = appendable
10
10
  end
11
11
 
@@ -14,22 +14,23 @@ module Spec
14
14
  context_or_collection.collection.each {|context| context.run(self)}
15
15
  end_run
16
16
  end
17
+
18
+ def spec(spec)
19
+ @specification_count += 1
20
+ @failed = false
21
+ end
17
22
 
18
23
  def pass(spec)
19
24
  @output << "."
20
- @expectation_count += 1
21
25
  end
22
26
 
23
27
  def failure(spec, exception)
24
- @output << "X"
25
- @expectation_count += 1
28
+ @output << "X" unless @failed
29
+ @failure_count += 1 unless @failed
30
+ @failed = true
26
31
  @failures << exception
27
32
  end
28
33
 
29
- def spec(spec)
30
- @specification_count += 1
31
- end
32
-
33
34
  def start_run
34
35
  @output << "\n"
35
36
  @start_time = Time.new
@@ -67,8 +68,7 @@ module Spec
67
68
 
68
69
  def dump_counts
69
70
  @output << "\n" << @specification_count.to_s << " specifications, "
70
- @output << @expectation_count.to_s << " expectations, "
71
- @output << @failures.length.to_s << " failures\n"
71
+ @output << @failure_count.to_s << " failures\n"
72
72
  end
73
73
 
74
74
  end
@@ -0,0 +1,48 @@
1
+ require 'test/unit'
2
+
3
+ require 'spec'
4
+
5
+ class DSLTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ $default_context = nil
9
+ $current_context = nil
10
+ end
11
+
12
+ def test_should_have_specification_method_defined
13
+ assert_equal true, Object.public_method_defined?(:specification)
14
+ end
15
+
16
+ def test_should_add_specification_to_default_context
17
+ specification('foobar') { true.should_equal true }
18
+
19
+ assert_equal true, $default_context.specifications.include?('foobar')
20
+ end
21
+
22
+ def test_should_create_new_context
23
+ context 'my_context'
24
+
25
+ assert_equal Spec::Context, $my_context.superclass
26
+ end
27
+
28
+ def test_should_set_current_context_to_new_context
29
+ context 'bar'
30
+
31
+ assert_equal $current_context, $bar
32
+ end
33
+
34
+ def test_should_add_bar_specification_to_foo_context
35
+ context 'foo'
36
+ specification('bar') { true.should_equal true }
37
+
38
+ assert_equal true, $foo.specifications.include?('bar')
39
+ end
40
+
41
+ def test_should_add_bar_specification_to_current_context
42
+ context 'foo'
43
+ specification('bar') { true.should_equal true }
44
+
45
+ assert_equal true, $current_context.specifications.include?('bar')
46
+ end
47
+
48
+ end
@@ -423,5 +423,45 @@ class ExpectationsTest < Test::Unit::TestCase
423
423
  self.should_be_false
424
424
  end
425
425
  end
426
+
427
+ # should_raise
428
+
429
+ def test_should_raise_should_pass_when_proper_exception_is_raised
430
+ assert_nothing_raised do
431
+ proc { ''.nonexistant_method }.should_raise(NoMethodError)
432
+ end
433
+ end
434
+
435
+ def test_should_raise_should_not_pass_when_wrong_exception_is_raised
436
+ assert_raise(Spec::Exceptions::ExpectationNotMetError) do
437
+ proc { ''.nonexistant_method }.should_raise(SyntaxError)
438
+ end
439
+ end
440
+
441
+ def test_should_raise_should_not_pass_with_no_exception
442
+ assert_raise(Spec::Exceptions::ExpectationNotMetError) do
443
+ not proc {''.to_s}.should_raise(NoMethodError)
444
+ end
445
+ end
446
+
447
+ # should_not_raise
448
+
449
+ def test_should_not_raise_should_pass_when_proper_exception_is_raised
450
+ assert_raise(Spec::Exceptions::ExpectationNotMetError) do
451
+ proc { ''.nonexistant_method }.should_not_raise(NoMethodError)
452
+ end
453
+ end
454
+
455
+ def test_should_not_raise_should_not_pass_when_wrong_exception_is_raised
456
+ assert_nothing_raised do
457
+ proc { ''.nonexistant_method }.should_not_raise(SyntaxError)
458
+ end
459
+ end
460
+
461
+ def test_should_not_raise_should_not_pass_with_no_exception
462
+ assert_nothing_raised do
463
+ not proc { ''.to_s }.should_not_raise(NoMethodError)
464
+ end
465
+ end
426
466
 
427
467
  end
@@ -0,0 +1,162 @@
1
+ require 'test/unit'
2
+ require 'socket'
3
+ require 'spec'
4
+
5
+ class PassingCon < Spec::Context
6
+
7
+ def ex1
8
+ true.should_be_true
9
+ end
10
+
11
+ def ex2
12
+ true.should_be_true
13
+ end
14
+
15
+ def ex3
16
+ true.should_be_true
17
+ end
18
+
19
+ end
20
+
21
+
22
+ class FailingCon < Spec::Context
23
+
24
+ def fail1
25
+ false.should_be_true
26
+ end
27
+
28
+ def fail2
29
+ false.should_be_true
30
+ end
31
+
32
+ def fail3
33
+ false.should_be_true
34
+ end
35
+
36
+ end
37
+
38
+
39
+ class ErringCon < Spec::Context
40
+
41
+ def error1
42
+ raise "boom"
43
+ end
44
+
45
+ def error2
46
+ raise "boom"
47
+ end
48
+
49
+ def error3
50
+ raise "boom"
51
+ end
52
+
53
+ end
54
+
55
+ class SocketListener
56
+
57
+ def initialize(port)
58
+ @server_socket = TCPServer.new("127.0.0.1", port)
59
+ @expectations = Array.new
60
+ @next_expectation_index = 0
61
+ @expectations_met = false
62
+ end
63
+
64
+ def shutdown
65
+ @th.kill
66
+ @server_socket.shutdown
67
+ end
68
+
69
+ def expects(expected_regex)
70
+ @expectations << expected_regex
71
+ end
72
+
73
+ def verify
74
+ sleep 1
75
+ return if @expectations_met
76
+ msg = "Nothing matching /#{@expectations[@next_expectation_index].source}/ was seen"
77
+ raise Test::Unit::AssertionFailedError.new(msg)
78
+ end
79
+
80
+ def run
81
+ @socket = @server_socket.accept
82
+ @th = Thread.new("socket listener") do
83
+ until @expectations_met
84
+ msg, sender = @socket.readline
85
+ msg.chomp!
86
+ next unless @expectations[@next_expectation_index] =~ msg
87
+ @next_expectation_index += 1
88
+ remaining = @expectations.size - @next_expectation_index
89
+ @expectations_met = (remaining == 0)
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ class TestGuiRunner < Test::Unit::TestCase
96
+
97
+ def setup
98
+ port = 13001
99
+ @listener = SocketListener.new(port)
100
+ @listener.expects /connected/
101
+ @runner = Spec::GuiRunner.new(port)
102
+ end
103
+
104
+ def teardown
105
+ @listener.shutdown
106
+ end
107
+
108
+ def test_size_on_start
109
+ @listener.expects /start 3/
110
+ @listener.run
111
+ @runner.run(PassingCon)
112
+ @listener.verify
113
+ end
114
+
115
+ def test_passing_example_outputs_period
116
+ @listener.expects /start/
117
+ @listener.expects /passed/
118
+ @listener.expects /passed/
119
+ @listener.expects /passed/
120
+ @listener.expects /end/
121
+ @listener.run
122
+ @runner.run(PassingCon)
123
+ @listener.verify
124
+ end
125
+
126
+ def test_failing_example_outputs_X
127
+ @listener.expects /start/
128
+ @listener.expects /failed/
129
+ @listener.expects /failed/
130
+ @listener.expects /failed/
131
+ @listener.expects /end/
132
+ @listener.run
133
+ @runner.run(FailingCon)
134
+ @listener.verify
135
+ end
136
+
137
+ def test_erring_example_outputs_X
138
+ @listener.expects /start/
139
+ @listener.expects /failed/
140
+ @listener.expects /failed/
141
+ @listener.expects /failed/
142
+ @listener.expects /end/
143
+ @listener.run
144
+ @runner.run(ErringCon)
145
+ @listener.verify
146
+ end
147
+
148
+ def test_failure_backtrace
149
+ @listener.expects /.*in `fail1'.*/
150
+ @listener.run
151
+ @runner.run(FailingCon)
152
+ @listener.verify
153
+ end
154
+
155
+ def test_error_backtrace
156
+ @listener.expects /.*in `error3'.*/
157
+ @listener.run
158
+ @runner.run(ErringCon)
159
+ @listener.verify
160
+ end
161
+
162
+ end
@@ -13,7 +13,7 @@ class MockTest < Test::Unit::TestCase
13
13
  begin
14
14
  @mock.__verify
15
15
  rescue Spec::Exceptions::MockExpectationError => e
16
- e.message.should_equal "./test/mock_test.rb:11:in `test_should_report_line_number_of_expectaion_of_unreceived_message': Mock 'test mock' expected wont_happen(<x:String>, <3:Fixnum>) 1 times, but received it 0 times"
16
+ e.message.should_equal "./test/mock_test.rb:11:in `test_should_report_line_number_of_expectaion_of_unreceived_message': Mock 'test mock' expected wont_happen(<x:String>, <3:Fixnum>) once, but received it 0 times"
17
17
  end
18
18
 
19
19
  end
@@ -89,4 +89,69 @@ class MockTest < Test::Unit::TestCase
89
89
  end
90
90
  end
91
91
 
92
+ def test_two_return_values
93
+ @mock.should_receive(:multi_call).twice.with_no_args.and_return_consecutively([1, 2])
94
+ assert_equal(1, @mock.multi_call)
95
+ assert_equal(2, @mock.multi_call)
96
+ @mock.__verify
97
+ end
98
+
99
+ def test_repeating_final_return_value
100
+ @mock.should_receive(:multi_call).at_least_once.with_no_args.and_return_consecutively([1, 2])
101
+ assert_equal(1, @mock.multi_call)
102
+ assert_equal(2, @mock.multi_call)
103
+ assert_equal(2, @mock.multi_call)
104
+ @mock.__verify
105
+ end
106
+
107
+ def test_should_throw_on_call_of_never_method
108
+ @mock.should_receive(:random_call).never
109
+ assert_raise(Spec::Exceptions::MockExpectationError) do
110
+ @mock.random_call
111
+ @mock.__verify
112
+ end
113
+ end
114
+
115
+ def test_should_throw_if_at_least_once_method_not_called
116
+ @mock.should_receive(:random_call).at_least_once
117
+ assert_raise(Spec::Exceptions::MockExpectationError) do
118
+ @mock.__verify
119
+ end
120
+ end
121
+
122
+ def test_should_not_throw_if_any_number_of_times_method_not_called
123
+ @mock.should_receive(:random_call).any_number_of_times
124
+ @mock.__verify
125
+ end
126
+
127
+ def test_should_not_throw_if_any_number_of_times_method_is_called
128
+ @mock.should_receive(:random_call).any_number_of_times
129
+ @mock.random_call
130
+ @mock.__verify
131
+ end
132
+
133
+ def test_should_not_throw_if_at_least_once_method_is_called_twice
134
+ @mock.should_receive(:random_call).at_least_once
135
+ @mock.random_call
136
+ @mock.random_call
137
+ @mock.__verify
138
+ end
139
+
140
+ def test_should_support_mutiple_calls_with_different_args
141
+ @mock.should_receive(:random_call).once.with(1)
142
+ @mock.should_receive(:random_call).once.with(2)
143
+ @mock.random_call(1)
144
+ @mock.random_call(2)
145
+ @mock.__verify
146
+ end
147
+
148
+ def test_should_support_multiple_calls_with_different_args_and_counts
149
+ @mock.should_receive(:random_call).twice.with(1)
150
+ @mock.should_receive(:random_call).once.with(2)
151
+ @mock.random_call(1)
152
+ @mock.random_call(2)
153
+ @mock.random_call(1)
154
+ @mock.__verify
155
+ end
156
+
92
157
  end
@@ -0,0 +1,29 @@
1
+ require 'test/unit'
2
+
3
+ require 'spec'
4
+
5
+
6
+ class AddToMe < Spec::Context
7
+
8
+ def orig_spec
9
+ true.should_equal true
10
+ end
11
+
12
+ end
13
+
14
+
15
+ class SpecificationAdditionTest < Test::Unit::TestCase
16
+
17
+ def test_should_add_foo_specification_to_context
18
+ AddToMe.add_specification(:added_spec) { false.should_equal false }
19
+
20
+ assert_equal true, AddToMe.specifications.include?('added_spec')
21
+ end
22
+
23
+ def test_should_add_another_specification_to_context
24
+ AddToMe.add_specification(:another_added_spec) { true.should_equal false }
25
+
26
+ assert_equal true, AddToMe.specifications.include?('another_added_spec')
27
+ end
28
+
29
+ end
@@ -97,19 +97,44 @@ class TestTextRunner < Test::Unit::TestCase
97
97
 
98
98
  def test_summary_with_no_failures
99
99
  @runner.run(PassingCon)
100
- assert_buffer_includes "3 specifications, 3 expectations, 0 failures"
100
+ assert_buffer_includes "3 specifications, 0 failures"
101
101
  end
102
102
 
103
103
  def test_should_run_all_specifications
104
104
  @runner.run(Spec::Collector)
105
- assert_buffer_includes "9 specifications, 9 expectations, 6 failures"
105
+ assert_buffer_includes "9 specifications, 6 failures"
106
106
  end
107
107
 
108
108
  def test_should_run_all_specifications_when_no_args_provided
109
109
  @runner.run
110
- assert_buffer_includes "9 specifications, 9 expectations, 6 failures"
110
+ assert_buffer_includes "9 specifications, 6 failures"
111
111
  end
112
112
 
113
+ def test_should_only_report_first_failure
114
+ ex = Spec::Exceptions::ExpectationNotMetError.new("1")
115
+ ex.set_backtrace(["1", "2"])
116
+ @runner.start_run
117
+ @runner.spec(nil)
118
+ @runner.failure(nil, ex)
119
+ @runner.failure(nil, ex)
120
+ @runner.end_run
121
+ assert_buffer_includes "1 specifications, 1 failure"
122
+ end
123
+
124
+ def test_should_only_report_first_failure_over_multiple_specs
125
+ ex = Spec::Exceptions::ExpectationNotMetError.new("1")
126
+ ex.set_backtrace(["1", "2"])
127
+ @runner.start_run
128
+ @runner.spec(nil)
129
+ @runner.failure(nil, ex)
130
+ @runner.failure(nil, ex)
131
+ @runner.spec(nil)
132
+ @runner.failure(nil, ex)
133
+ @runner.failure(nil, ex)
134
+ @runner.end_run
135
+ assert_buffer_includes "2 specifications, 2 failure"
136
+ end
137
+
113
138
  def assert_buffer_includes(substring)
114
139
  assert(@buffer.include?(substring), _buffer_message(substring))
115
140
  end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rspec
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2005-09-27
6
+ version: 0.3.0
7
+ date: 2006-01-20 00:00:00 -08:00
8
8
  summary: Behaviour Specification Framework for Ruby
9
9
  require_paths:
10
10
  - lib
@@ -27,33 +27,41 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
27
27
  version: 0.0.0
28
28
  version:
29
29
  platform: ruby
30
+ signing_key:
31
+ cert_chain:
30
32
  authors:
31
33
  - Steven Baker
32
34
  files:
33
35
  - CHANGES
34
- - MT
35
- - Rakefile.rb
36
+ - Rakefile
36
37
  - README
37
38
  - TODO
38
39
  - TUTORIAL
39
40
  - lib/spec.rb
40
41
  - lib/spec/collector.rb
41
42
  - lib/spec/context.rb
43
+ - lib/spec/dsl.rb
42
44
  - lib/spec/exceptions.rb
43
45
  - lib/spec/expectations.rb
46
+ - lib/spec/gui_runner.rb
44
47
  - lib/spec/mock.rb
45
48
  - lib/spec/text_runner.rb
46
49
  - test/context_fixtures_test.rb
47
50
  - test/context_run_test.rb
51
+ - test/dsl_test.rb
48
52
  - test/error_reporting_test.rb
49
53
  - test/expectations_test.rb
50
54
  - test/get_classes.rb
55
+ - test/gui_runner_test.rb
51
56
  - test/mock_test.rb
52
57
  - test/spec_collection_test.rb
58
+ - test/specification_addition_test.rb
53
59
  - test/specification_identification_test.rb
54
60
  - test/text_runner_test.rb
61
+ - examples/add_specification_spec.rb
55
62
  - examples/craps.rb
56
63
  - examples/craps_spec.rb
64
+ - examples/dsl_spec.rb
57
65
  - examples/movie.rb
58
66
  - examples/movie_list.rb
59
67
  - examples/movie_spec.rb