test-spec 0.2

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ setup.rb
6
+ lib/test-spec.rb
7
+ lib/test-spec/version.rb
8
+ lib/test-spec/test/spec.rb
9
+ lib/test-spec/test/spec/dox.rb
10
+ lib/test-spec/test/spec/rdox.rb
11
+ lib/test-spec/test/spec/should-output.rb
@@ -0,0 +1,206 @@
1
+ = test/spec, a BDD interface for Test::Unit
2
+
3
+ Copyright (C) 2006 Christian Neukirchen <mailto:chneukirchen@gmail.com>
4
+
5
+
6
+ == What is test/spec?
7
+
8
+ test/spec layers an RSpec-inspired interface on top of Test::Unit, so
9
+ you can mix TDD and BDD (Behavior-Driven Development).
10
+
11
+ test/spec is a clean-room implementation that maps most kinds of
12
+ Test::Unit assertions to a `should'-like syntax.
13
+
14
+ Consider this Test::Unit test case:
15
+
16
+ class TestFoo < Test::Unit::TestCase
17
+ def test_should_bar
18
+ assert_equal 5, 2 + 3
19
+ end
20
+ end
21
+
22
+ In test/spec, it looks like this:
23
+
24
+ require 'test/spec'
25
+
26
+ context "Foo" do
27
+ specify "should bar" do
28
+ (2 + 3).should.equal 5
29
+ end
30
+ end
31
+
32
+ test/spec does not include a mocking/stubbing-framework; use whichever
33
+ you like to. test/spec has been tested successfully with FlexMock and
34
+ Mocha.
35
+
36
+ test/spec has no dependencies outside Ruby 1.8.
37
+
38
+
39
+ == Mixing test/spec and test/unit
40
+
41
+ test/spec and Test::Unit contexts/test cases can be intermixed freely,
42
+ run in the same test and live in the same files. You can just add them
43
+ to your Rake::TestTask, too. test/spec allows you to leverage your
44
+ full existing Test::Unit infrastructure.
45
+
46
+ test/spec does not change Test::Unit with the exception of
47
+ monkey-patching Test::Unit::TestSuite to order the test cases before
48
+ running them. (This should not do any harm, but if you know a way
49
+ around it, please tell me.)
50
+
51
+ test/spec adds two global methods, Object#should and Kernel.context.
52
+
53
+ You can use <tt>assert_*</tt> freely in specify-blocks; Object#should
54
+ works in plain Test::Unit test cases, too, but they will not be counted.
55
+
56
+
57
+ == Wrapped assertions
58
+
59
+ +assert_equal+:: <tt>should.equal</tt>, <tt>should ==</tt>
60
+ +assert_not_equal+:: <tt>should.not.equal</tt>, <tt>should.not ==</tt>
61
+ +assert_same+:: <tt>should.be</tt>
62
+ +assert_not_same+:: <tt>should.not.be</tt>
63
+ +assert_nil+:: <tt>should.be.nil</tt>
64
+ +assert_not_nil+:: <tt>should.not.be.nil</tt>
65
+ +assert_in_delta+:: <tt>should.be.close</tt>
66
+ +assert_match+:: <tt>should.match</tt>, <tt>should =~</tt>
67
+ +assert_no_match+:: <tt>should.not.match</tt>, <tt>should.not =~</tt>
68
+
69
+ +assert_instance_of+:: <tt>should.be.an.instance_of</tt>
70
+ +assert_kind_of+:: <tt>should.be.a.kind_of</tt>
71
+ +assert_respond_to+:: <tt>should.respond_to</tt>
72
+ +assert_raise+:: <tt>should.raise</tt>
73
+ +assert_nothing_raised+:: <tt>should.not.raise</tt>
74
+ +assert_throws+:: <tt>should.throw</tt>
75
+ +assert_nothing_thrown+:: <tt>should.not.throw</tt>
76
+
77
+ +assert_block+:: <tt>should.satisfy</tt>
78
+
79
+ (+a+, +an+ and +be+ without arguments are optional and no-ops.)
80
+
81
+
82
+ == Additional assertions
83
+
84
+ These assertions are not included in Test::Unit, but have been added
85
+ to test/spec for convenience:
86
+
87
+ * <tt>should.not.satisfy</tt>
88
+ * <tt>should.include</tt>
89
+ * <tt>a.should.</tt>_predicate_ (works like <tt>assert
90
+ a.</tt>_predicate_<tt>?</tt>)
91
+ * <tt>a.should.be </tt>_operator_ (where _operator_ is one of <tt>></tt>, <tt>>=</tt>, <tt><</tt>, <tt><=</tt> or <tt>===</tt>)
92
+ * <tt>should.output</tt> (require test/spec/should-output)
93
+
94
+ If you write an useful general-purpose assertion, I'd like to hear of
95
+ it and may add it to the test/spec distribution.
96
+
97
+
98
+ == SpecDox and RDox
99
+
100
+ test/spec adds two additional test runners to Test::Unit, based on the
101
+ console runner but with a different output format.
102
+
103
+ SpecDox, run with <tt>--runner=specdox</tt> (or <tt>-rs</tt>) looks
104
+ like RSpec's output:
105
+
106
+ should.output
107
+ - works for print
108
+ - works for puts
109
+ - works with readline
110
+
111
+ RDox, run with <tt>--runner=rdox</tt> (or <tt>-rr</tt>) can be
112
+ included for RDoc documentation (e.g. see SPECS):
113
+
114
+ == should.output
115
+ * works for print
116
+ * works for puts
117
+ * works with readline
118
+
119
+ SpecDox and RDox work for Test::Unit too:
120
+
121
+ $ ruby -r test/spec test/testunit/test_testresult.rb -rs
122
+
123
+ Test::Unit::TC_TestResult
124
+ - fault notification
125
+ - passed?
126
+ - result changed notification
127
+
128
+ Finished in 0.106647 seconds.
129
+
130
+ 3 specifications (30 requirements), 0 failures
131
+
132
+
133
+ == specrb
134
+
135
+ Since version 0.2, test/spec features a standalone test runner called
136
+ specrb. specrb is like an extended version of testrb, Test::Unit's
137
+ test runner, but has additional options. It can be used for
138
+ plain Test::Unit suites, too.
139
+
140
+ $ specrb -a -s -n should.output
141
+
142
+ should.output
143
+ - works for print
144
+ - works for puts
145
+ - works with readline
146
+
147
+ Finished in 0.162571 seconds.
148
+
149
+ 3 specifications (6 requirements), 0 failures
150
+
151
+ Run <tt>specrb --help</tt> for the usage.
152
+
153
+
154
+ == History
155
+
156
+ * September 29th, 2006: First public release 0.1.
157
+
158
+ * October 18th, 2006: Second public release 0.2.
159
+ * Better, module-based implementation
160
+ * Official support for FlexMock and Mocha
161
+ * More robust Should#output
162
+ * Should#_operator_
163
+ * Nested contexts
164
+ * Standalone test/spec runner, specrb
165
+
166
+
167
+ == Contact
168
+
169
+ Please mail bugs, suggestions and patches to
170
+ <mailto:chneukirchen@gmail.com>.
171
+
172
+ Darcs repository ("darcs send" is welcome for patches):
173
+ http://chneukirchen.org/repos/testspec
174
+
175
+
176
+ == Thanks to
177
+
178
+ * Eero Saynatkari for writing <tt>should.output</tt>.
179
+ * Thomas Fuchs for script.aculo.us BDD testing which convinced me.
180
+ * Dave Astels for BDD.
181
+ * The RSpec team for API inspiration.
182
+ * Nathaniel Talbott for Test::Unit.
183
+
184
+
185
+ == Copying
186
+
187
+ Copyright (C) 2006 Christian Neukirchen <http://purl.org/net/chneukirchen>
188
+
189
+ test/spec is licensed under the same terms as Ruby itself.
190
+
191
+ Please mail bugs, feature requests or patches to the mail addresses
192
+ found above or use IRC[irc://freenode.net/#ruby-lang] to contact the
193
+ developer.
194
+
195
+
196
+ == Links
197
+
198
+ Behavior-Driven Development:: <http://behaviour-driven.org/>
199
+ RSpec:: <http://rspec.rubyforge.org/>
200
+ script.aculo.us testing:: <http://mir.aculo.us/articles/2006/08/29/bdd-style-javascript-testing>
201
+
202
+ FlexMock:: <http://onestepback.org/software/flexmock/>
203
+ Mocha:: <http://mocha.rubyforge.org/>
204
+
205
+ Christian Neukirchen:: <http://chneukirchen.org/>
206
+
@@ -0,0 +1,137 @@
1
+ # Rakefile for testspec. -*-ruby-*-
2
+ #
3
+ #
4
+ #
5
+ require 'rubygems'
6
+ require 'rake'
7
+ require 'rake/clean'
8
+ require 'rake/testtask'
9
+ require 'rake/packagetask'
10
+ require 'rake/gempackagetask'
11
+ require 'rake/rdoctask'
12
+ require 'rake/contrib/rubyforgepublisher'
13
+ require 'fileutils'
14
+ require 'hoe'
15
+ include FileUtils
16
+ require File.join(File.dirname(__FILE__), 'lib', 'test-spec', 'version')
17
+
18
+ AUTHOR = "Christian Neukirchen" # can also be an array of Authors
19
+ EMAIL = "chneukirchen@gmail.com"
20
+ DESCRIPTION = "Behaviour Driven Development with Test::Unit"
21
+ GEM_NAME = "test-spec" # what ppl will type to install your gem
22
+ RUBYFORGE_PROJECT = "test-spec" # The unix name for your project
23
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
24
+ RELEASE_TYPES = %w( gem ) # can use: gem, tar, zip
25
+
26
+
27
+ NAME = "test-spec"
28
+ REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
29
+ VERS = ENV['VERSION'] || (TestSpec::VERSION::STRING + (REV ? ".#{REV}" : ""))
30
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
31
+ RDOC_OPTS = ['--quiet', '--title', "test_spec documentation",
32
+ "--opname", "index.html",
33
+ "--line-numbers",
34
+ "--main", "README",
35
+ "--inline-source"]
36
+
37
+ # Generate all the Rake tasks
38
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
39
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
40
+ p.author = AUTHOR
41
+ p.description = DESCRIPTION
42
+ p.email = EMAIL
43
+ p.summary = DESCRIPTION
44
+ p.url = HOMEPATH
45
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
46
+ p.test_globs = ["test/**/*_test.rb"]
47
+ p.clean_globs = CLEAN #An array of file patterns to delete on clean.
48
+ p.extra_deps = ['flexmock','>= 0.4.3'],['mocha','>= 0.3.2']
49
+ # == Optional
50
+ #p.changes - A description of the release's latest changes.
51
+ #p.spec_extras - A hash of extra values to set in the gemspec.
52
+ end
53
+
54
+
55
+ desc "Run all the tests"
56
+ task :default => [:test]
57
+
58
+ desc "Do predistribution stuff"
59
+ task :predist => [:chmod, :changelog, :rdoc]
60
+
61
+
62
+ desc "Run all the tests"
63
+ task :test => :chmod do
64
+ ruby "bin/specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
65
+ end
66
+
67
+ desc "Make an archive as .tar.gz"
68
+ task :dist => :test do
69
+ system "export DARCS_REPO=#{File.expand_path "."}; " +
70
+ "darcs dist -d testspec#{get_darcs_tree_version}"
71
+ end
72
+
73
+ desc "Make binaries executable"
74
+ task :chmod do
75
+ Dir["bin/*"].each { |binary| File.chmod(0775, binary) }
76
+ end
77
+
78
+ desc "Generate a ChangeLog"
79
+ task :changelog do
80
+ system "darcs changes --repo=#{ENV["DARCS_REPO"] || "."} >ChangeLog"
81
+ end
82
+
83
+ desc "Generate RDoc documentation"
84
+ Rake::RDocTask.new(:rdoc) do |rdoc|
85
+ rdoc.options << '--line-numbers' << '--inline-source'
86
+ rdoc.rdoc_dir = "rdoc"
87
+ rdoc.rdoc_files.include 'README'
88
+ rdoc.rdoc_files.include 'ROADMAP'
89
+ rdoc.rdoc_files.include 'SPECS'
90
+ rdoc.rdoc_files.include('lib/**/*.rb')
91
+ end
92
+ task :rdoc => "SPECS"
93
+
94
+ desc "Generate RDox"
95
+ task "SPECS" do
96
+ ruby "bin/specrb -Ilib:test -a --rdox >SPECS"
97
+ end
98
+
99
+ begin
100
+ require 'rcov/rcovtask'
101
+
102
+ Rcov::RcovTask.new do |t|
103
+ t.test_files = FileList['test/spec_*.rb'] + ['--', '-rs'] # evil
104
+ t.verbose = true # uncomment to see the executed command
105
+ t.rcov_opts = ["--text-report", "--include-file", "^lib\\|^test"]
106
+ end
107
+ rescue LoadError
108
+ end
109
+
110
+
111
+ # Helper to retrieve the "revision number" of the darcs tree.
112
+ def get_darcs_tree_version
113
+ return "" unless File.directory? "_darcs"
114
+
115
+ changes = `darcs changes`
116
+ count = 0
117
+ tag = "0.0"
118
+
119
+ changes.each("\n\n") { |change|
120
+ head, title, desc = change.split("\n", 3)
121
+
122
+ if title =~ /^ \*/
123
+ # Normal change.
124
+ count += 1
125
+ elsif title =~ /tagged (.*)/
126
+ # Tag. We look for these.
127
+ tag = $1
128
+ break
129
+ else
130
+ warn "Unparsable change: #{change}"
131
+ end
132
+ }
133
+
134
+ "-" + tag + "." + count.to_s
135
+ end
136
+
137
+
@@ -0,0 +1,2 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'test-spec/**/*.rb')].sort.each { |lib| require lib }
2
+ require 'rubygems'
@@ -0,0 +1,368 @@
1
+ #
2
+ # test/spec -- a BDD interface for Test::Unit
3
+ #
4
+ # Copyright (C) 2006 Christian Neukirchen <mailto:chneukirchen@gmail.com>
5
+ #
6
+ # This work is licensed under the same terms as Ruby itself.
7
+ #
8
+
9
+ require 'test/unit'
10
+
11
+ class Test::Unit::AutoRunner # :nodoc:
12
+ RUNNERS[:specdox] = lambda {
13
+ require 'test/spec/dox'
14
+ Test::Unit::UI::SpecDox::TestRunner
15
+ }
16
+
17
+ RUNNERS[:rdox] = lambda {
18
+ require 'test/spec/rdox'
19
+ Test::Unit::UI::RDox::TestRunner
20
+ }
21
+ end
22
+
23
+ module Test # :nodoc:
24
+ end
25
+
26
+ module Test::Spec
27
+ VERSION = "0.1"
28
+
29
+ CONTEXTS = {}
30
+
31
+ class DefinitionError < StandardError
32
+ end
33
+
34
+ class Should
35
+ include Test::Unit::Assertions
36
+
37
+ def initialize(object)
38
+ @object = object
39
+ end
40
+
41
+ $TEST_SPEC_TESTCASE = nil
42
+ def add_assertion
43
+ $TEST_SPEC_TESTCASE && $TEST_SPEC_TESTCASE.__send__(:add_assertion)
44
+ end
45
+
46
+
47
+ def an
48
+ self
49
+ end
50
+
51
+ def a
52
+ self
53
+ end
54
+
55
+ def not
56
+ ShouldNot.new(@object)
57
+ end
58
+
59
+
60
+ def satisfy(&block)
61
+ assert_block("satisfy block failed.") {
62
+ yield @object
63
+ }
64
+ end
65
+
66
+ def equal(value)
67
+ assert_equal value, @object
68
+ end
69
+ alias == equal
70
+
71
+ def close(value, delta)
72
+ assert_in_delta value, @object, delta
73
+ end
74
+ alias be_close close
75
+
76
+ def be(*value)
77
+ case value.size
78
+ when 0
79
+ self
80
+ when 1
81
+ assert_same value.first, @object
82
+ else
83
+ raise ArgumentError, "should.be needs zero or one argument"
84
+ end
85
+ end
86
+
87
+ def match(value)
88
+ assert_match value, @object
89
+ end
90
+ alias =~ match
91
+
92
+ def instance_of(klass)
93
+ assert_instance_of klass, @object
94
+ end
95
+ alias be_an_instance_of instance_of
96
+
97
+ def kind_of(klass)
98
+ assert_kind_of klass, @object
99
+ end
100
+ alias be_a_kind_of kind_of
101
+
102
+ def respond_to(method)
103
+ assert_respond_to @object, method
104
+ end
105
+
106
+ def _raise(*args)
107
+ args = [RuntimeError] if args.empty?
108
+ assert_raise(*args, &@object)
109
+ end
110
+
111
+ def throw(*args)
112
+ assert_throws(*args, &@object)
113
+ end
114
+
115
+ def nil
116
+ assert_nil @object
117
+ end
118
+ alias be_nil nil
119
+
120
+
121
+ def include(value)
122
+ msg = build_message(nil, "<?> expected to include ?, but it didn't.",
123
+ @object, value)
124
+ assert_block(msg) { @object.include?(value) }
125
+ end
126
+
127
+ def >(value)
128
+ assert_operator @object, :>, value
129
+ end
130
+
131
+ def >=(value)
132
+ assert_operator @object, :>=, value
133
+ end
134
+
135
+ def <(value)
136
+ assert_operator @object, :<, value
137
+ end
138
+
139
+ def <=(value)
140
+ assert_operator @object, :<=, value
141
+ end
142
+
143
+ def ===(value)
144
+ assert_operator @object, :===, value
145
+ end
146
+
147
+ def method_missing(name, *args)
148
+ # This will make raise call Kernel.raise, and self.raise call _raise.
149
+ return _raise(*args) if name == :raise
150
+
151
+ if @object.respond_to?("#{name}?")
152
+ assert @object.__send__("#{name}?", *args),
153
+ "#{name}? expected to be true."
154
+ else
155
+ super
156
+ end
157
+ end
158
+ end
159
+
160
+ class ShouldNot
161
+ include Test::Unit::Assertions
162
+
163
+ def initialize(object)
164
+ @object = object
165
+ end
166
+
167
+ def equal(value)
168
+ assert_not_equal value, @object
169
+ end
170
+ alias == equal
171
+
172
+ def be(*value)
173
+ case value.size
174
+ when 0
175
+ self
176
+ when 1
177
+ assert_not_same value.first, @object
178
+ else
179
+ Kernel.raise ArgumentError, "should.be needs zero or one argument"
180
+ end
181
+ end
182
+
183
+ def match(value)
184
+ # Icky Regexp check
185
+ assert_no_match value, @object
186
+ end
187
+ alias =~ match
188
+
189
+ def _raise(*args)
190
+ assert_nothing_raised(*args, &@object)
191
+ end
192
+
193
+ def throw
194
+ assert_nothing_thrown(&@object)
195
+ end
196
+
197
+ def nil
198
+ assert_not_nil @object
199
+ end
200
+ alias be_nil nil
201
+
202
+
203
+ def not
204
+ Should.new(@object)
205
+ end
206
+
207
+
208
+ def method_missing(name, *args)
209
+ # This will make raise call Kernel.raise, and self.raise call _raise.
210
+ return _raise(*args) if name == :raise
211
+
212
+ if @object.respond_to?("#{name}?")
213
+ assert_block("#{name}? expected to be false.") {
214
+ not @object.__send__("#{name}?", *args)
215
+ }
216
+ else
217
+ super
218
+ end
219
+ end
220
+
221
+ end
222
+ end
223
+
224
+ class Test::Spec::TestCase
225
+ attr_reader :testcase
226
+ attr_reader :name
227
+ attr_reader :position
228
+
229
+ module InstanceMethods
230
+ def setup # :nodoc:
231
+ $TEST_SPEC_TESTCASE = self
232
+ super
233
+ self.class.setups.each { |s| instance_eval(&s) }
234
+ end
235
+
236
+ def teardown # :nodoc:
237
+ super
238
+ self.class.teardowns.each { |t| instance_eval(&t) }
239
+ end
240
+
241
+ def initialize(name)
242
+ super name
243
+
244
+ # Don't let the default_test clutter up the results and don't
245
+ # flunk if no tests given, either.
246
+ throw :invalid_test if name == :default_test
247
+ end
248
+
249
+ def position
250
+ self.class.position
251
+ end
252
+
253
+ def context(*args)
254
+ raise Test::Spec::DefinitionError,
255
+ "context definition is not allowed inside a specify-block"
256
+ end
257
+ end
258
+
259
+ module ClassMethods
260
+ attr_accessor :count
261
+ attr_accessor :name
262
+ attr_accessor :position
263
+ attr_accessor :parent
264
+
265
+ attr_accessor :setups
266
+ attr_accessor :teardowns
267
+
268
+ def context(name, &block)
269
+ (Test::Spec::CONTEXTS[self.name + "\t" + name] ||=
270
+ Test::Spec::TestCase.new(name, self)).add(&block)
271
+ end
272
+
273
+ def specify(specname, &block)
274
+ raise ArgumentError, "specify needs a block" if block.nil?
275
+
276
+ self.count += 1 # Let them run in order of definition
277
+
278
+ # TODO Changed by Jean-Michel
279
+ # define_method("test_%03d: %s" % [count, specname], &block)
280
+ define_method("test_spec {%s} %03d [%s]" % [name, count, specname], &block)
281
+ end
282
+
283
+ def setup(&block)
284
+ setups << block
285
+ end
286
+
287
+ def teardown(&block)
288
+ teardowns << block
289
+ end
290
+
291
+ def init(name, position, parent)
292
+ self.position = position
293
+ self.parent = parent
294
+
295
+ if parent
296
+ self.name = parent.name + "\t" + name
297
+ else
298
+ self.name = name
299
+ end
300
+
301
+ self.count = 0
302
+ self.setups = []
303
+ self.teardowns = []
304
+ end
305
+ end
306
+
307
+ @@POSITION = 0
308
+
309
+ def initialize(name, parent=nil)
310
+ @testcase = Class.new(Test::Unit::TestCase) {
311
+ include InstanceMethods
312
+ extend ClassMethods
313
+ }
314
+
315
+ @@POSITION = @@POSITION + 1
316
+ @testcase.init(name, @@POSITION, parent)
317
+ end
318
+
319
+ def add(&block)
320
+ raise ArgumentError, "context needs a block" if block.nil?
321
+
322
+ @testcase.class_eval(&block)
323
+ self
324
+ end
325
+ end
326
+
327
+
328
+ # Monkey-patch test/unit to run tests in an optionally specified order.
329
+ module Test::Unit # :nodoc:
330
+ class TestSuite # :nodoc:
331
+ undef run
332
+ def run(result, &progress_block)
333
+ sort!
334
+ yield(STARTED, name)
335
+ @tests.each do |test|
336
+ test.run(result, &progress_block)
337
+ end
338
+ yield(FINISHED, name)
339
+ end
340
+
341
+ def sort!
342
+ @tests = @tests.sort_by { |test|
343
+ test.respond_to?(:position) ? test.position : 0
344
+ }
345
+ end
346
+
347
+ def position
348
+ @tests.first.respond_to?(:position) ? @tests.first.position : 0
349
+ end
350
+ end
351
+ end
352
+
353
+
354
+ # Global helpers
355
+
356
+ class Object
357
+ def should
358
+ Test::Spec::Should.new(self)
359
+ end
360
+ end
361
+
362
+ module Kernel
363
+ def context(name, &block)
364
+ (Test::Spec::CONTEXTS[name] ||= Test::Spec::TestCase.new(name)).add(&block)
365
+ end
366
+
367
+ private :context
368
+ end