tst 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Arun Srinivasan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # Tst
2
+
3
+ A small testing library that tries not to do too much.
4
+
5
+ You get a top-level `tst` method, a few `assert` methods, and readable test
6
+ output.
7
+
8
+ # Installation
9
+
10
+ Standard issue:
11
+
12
+ gem install tst
13
+
14
+ # Usage
15
+
16
+ The `tst` method is available on the top-level, takes an optional name and
17
+ a required block, like so:
18
+
19
+ ```ruby
20
+ tst "Array-indexing is not 1-based" do
21
+ arr = [1, 2, 3, 4, 5]
22
+ assert arr[1] != 1
23
+ end
24
+ ```
25
+
26
+ If the block runs to completion without raising anything, it passes.
27
+
28
+ If one of assertion methods (see below) raises, it counts as a failure.
29
+
30
+ If the block raises for any other reason, it's an exception.
31
+
32
+ ### Assertions
33
+
34
+ Within a `tst` block, you have access to 3 assert methods: `assert`,
35
+ `assert_equal`, and `assert_raises`. They do what you'd expect.
36
+
37
+ If those don't cover your needs, just write your own:
38
+
39
+ ```ruby
40
+ module Tst
41
+
42
+ # The assertions live in the Assertions module and are available
43
+ # to the `tst` blocks.
44
+ module Assertions
45
+
46
+ # No naming or argument rules, it's just a method.
47
+ # Define it how you please.
48
+ def sums_to_42(*args)
49
+ actual = args.inject(0) { |sum, arg| sum + arg }
50
+
51
+ # return without raising to pass
52
+ return if actual == 42
53
+
54
+ # Tst::Failure is a subclass of StandardError that takes
55
+ # 3 arguments: Failure.new(message, expected, actual)
56
+ #
57
+ # message - a brief description of the failure
58
+ # expected - the expected value
59
+ # actual - the actual value
60
+ raise Tst::Failure.new("#{args} doesn't sum to 42", 42, actual)
61
+ end
62
+ end
63
+ end
64
+
65
+ # Now just use your custom assertion in a test
66
+ tst "some array sums to 42" do
67
+ sums_to_42(40, 1, 1) # This will pass just fine
68
+ sums_to_42(40, 1, 2) # This will fail
69
+ end
70
+ ```
71
+
72
+ Running that test will yield output like:
73
+
74
+ $ tst 'some_array.rb'
75
+ F
76
+ F1: [40, 1, 2] doesn't sum to 42 - "some array sums to 42"
77
+ exp: 42
78
+ act: 43
79
+ * some_array.rb:<lineno>:in `block in <top (required)>'
80
+
81
+ ### Running tests
82
+
83
+ Tst comes with a command-line runner, named "tst".
84
+
85
+ $ tst
86
+ Usage: tst [options] files
87
+
88
+ -h, --help Displays this message
89
+
90
+ -I, --libdir A directory to add to the load path
91
+ (can be used multiple times: "-I lib -I test"
92
+
93
+ -r, --reporter Specify a reporter to use. "pretty" or "plain".
94
+ Defaults to "plain"
95
+
96
+ files Specifies which test files to run. Is passed through to
97
+ Dir.glob, so anything it accepts is valid. NOTE: use
98
+ quotes to make sure nothing gets swallowed.
99
+
100
+ You can also run tests programmatically. Here's a sample rake task, for
101
+ instance:
102
+
103
+ ```ruby
104
+ require 'tst'
105
+
106
+ desc 'Run the tests'
107
+ task :test do
108
+ Tst.run 'test/*.rb',
109
+ :load_paths => ['.', 'lib'], # same as -I above
110
+ :reporter => Tst::Reporters::Pretty.new # same as -r above
111
+ end
112
+ ```
113
+
114
+ # Meanderings
115
+
116
+ ### Why aren't there setup and teardown methods?
117
+
118
+ Having setup and teardown methods makes it harder to look at one test and see
119
+ what's happening, as it's easy for relevant stuff to be scrolled off the
120
+ screen, or even hidden in a superclass. And the names are totally generic.
121
+
122
+ And we're in ruby! You can get what you need by just writing a method _with
123
+ a name_ that encapsulates what you need. Take a look at
124
+ "test/plain_reporter.rb" for an example.
125
+
126
+ ### Lightness
127
+
128
+ Tst tries to avoid weight, in various forms:
129
+
130
+ **Weighty Structure**
131
+
132
+ Tst has no grouping - TestCases, Context blocks, etc, because we're in ruby,
133
+ where such ceremony is unnecessary. It is not required to make the testing
134
+ tools work, and I'm not sure it yields much benefit. We already put our test
135
+ code into files with names. Is further taxonomy really necessary?
136
+
137
+ I believe the lack of ceremony is easier to understand, learn, use, and is
138
+ nicer to look at.
139
+
140
+ **Conceptual weight**
141
+
142
+ This may just be a personal preference thing, but I don't understand the appeal
143
+ of the 'english-y' DSL's.
144
+
145
+ ```ruby
146
+ # Test tool concepts
147
+ # ------------------
148
+ # Compare this...
149
+ tst "a new Thing is not empty" do # :tst
150
+ thing = Thing.new
151
+ assert !thing.empty? # :assert
152
+ end
153
+
154
+ # ... to this (RSpec):
155
+ describe Thing do # :describe
156
+ let(:thing) { Thing.new } # :let
157
+
158
+ it "should be empty" do # :it
159
+ thing.should be_empty # :should
160
+ end # magic :be_* method
161
+ # how :thing gets in scope
162
+ end
163
+
164
+ # or with even more magic:
165
+ describe Thing do # :describe
166
+ it { should be_empty } # :it
167
+ end # magic Thing initialization
168
+ # implicit :subject
169
+ # :should (w/ implicit :subject)
170
+ # :be_*
171
+ ```
172
+
173
+ Some argue that the RSpec examples read well, but look at how much you need to
174
+ know to understand what's going on.
175
+
176
+ Don't get me wrong. RSpec is a pretty cool implementation of an interesting
177
+ idea, but I'm not convinced that it's worth the conceptual overhead when you
178
+ consider what you get.
179
+
180
+ **Code weight**
181
+
182
+ Just looking in the 'lib' directories:
183
+
184
+ - test/unit: 6313
185
+ - rspec: 5460
186
+ - minitest: 1210
187
+ - tst: 220
188
+
189
+ The core of tst's testing engine is really just 122 lines (see 'tst.rb'). The
190
+ other 98 is IO/reporting code.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ $:.unshift File.expand_path('lib')
2
+ require 'tst'
3
+
4
+ version = Tst::VERSION
5
+
6
+ desc 'Run the tests'
7
+ task :test do
8
+ Tst.run 'test/*.rb',
9
+ :load_paths => ['.', 'lib'],
10
+ :reporter => Tst::Reporters::Pretty.new
11
+ end
12
+
13
+ namespace :test do
14
+ desc 'For fun, run the fixtures'
15
+ task :fixtures do
16
+ Tst.run 'test/fixtures/*.rb',
17
+ :load_paths => ['.', 'lib'],
18
+ :reporter => Tst::Reporters::Pretty.new
19
+ end
20
+ end
21
+
22
+ namespace :gem do
23
+ desc 'Clean up generated files'
24
+ task :clean do
25
+ sh 'rm -rf pkg'
26
+ end
27
+
28
+ desc "Build the gem"
29
+ task :build => :clean do
30
+ sh "mkdir pkg"
31
+ sh "gem build tst.gemspec"
32
+ sh "mv tst-#{version}.gem pkg/"
33
+ end
34
+
35
+ desc "Release v#{version}"
36
+ task :release => :build do
37
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
38
+ sh "git tag v#{version}"
39
+ sh "git push origin master"
40
+ sh "git push origin v#{version}"
41
+ sh "gem push pkg/#{name}-#{version}.gem"
42
+ end
43
+ end
44
+
45
+ task :default => :test
data/bin/tst ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('lib')
4
+ require 'getoptlong'
5
+ require 'tst'
6
+
7
+ Banner = <<-Banner
8
+ Usage: tst [options] files
9
+
10
+ -h, --help Displays this message
11
+
12
+ -I, --libdir A directory to add to the load path
13
+ (can be used multiple times: "-I lib -I test"
14
+
15
+ -r, --reporter Specify a reporter to use. "pretty" or "plain".
16
+ Defaults to "plain"
17
+
18
+ files Specifies which test files to run. Is passed through to
19
+ Dir.glob, so anything it accepts is valid. NOTE: use
20
+ quotes to make sure nothing gets swallowed.
21
+ Banner
22
+
23
+ def parse_options!(options)
24
+ GetoptLong.new(
25
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
26
+ ['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT],
27
+ ['--reporter', '-r', GetoptLong::REQUIRED_ARGUMENT]
28
+ ).each do |switch, arg|
29
+ case switch
30
+ when '--help'
31
+ exit_with(Banner)
32
+ when '--reporter'
33
+ set_reporter(arg, options) || exit_with("Unknown reporter '#{arg}'.")
34
+ when '--libdir'
35
+ add_include_dir(arg, options)
36
+ end
37
+ end
38
+
39
+ exit_with(Banner) unless glob = ARGV.shift
40
+ return glob, options
41
+ end
42
+
43
+ def set_reporter(reporter, options)
44
+ case reporter
45
+ when "pretty"
46
+ options[:reporter] = Tst::Reporters::Pretty.new
47
+ true
48
+ when "plain"
49
+ true
50
+ else
51
+ false
52
+ end
53
+ end
54
+
55
+ def add_include_dir(dir, options)
56
+ options[:load_paths] << dir
57
+ end
58
+
59
+ def exit_with(msg)
60
+ puts msg
61
+ exit
62
+ end
63
+
64
+ glob, options = parse_options!(
65
+ :reporter => Tst::Reporters::Plain.new,
66
+ :load_paths => []
67
+ )
68
+
69
+ Tst.run(glob, options)
@@ -0,0 +1,87 @@
1
+ module Tst
2
+ module Reporters
3
+ class Plain
4
+ attr_reader :io, :results
5
+
6
+ def initialize(io = $stdout)
7
+ @io = io
8
+ end
9
+
10
+ def success(result) io.print('.') end
11
+ def failure(result) io.print('F') end
12
+ def exception(result) io.print('E') end
13
+
14
+ def summarize(results)
15
+ @results = results
16
+ summary = ["\n"] + failures + exceptions + conclusion
17
+ summary.each { |line| io.puts(line) }
18
+ end
19
+
20
+ def failures
21
+ results.failures.each_with_index.flat_map do |failure, i|
22
+ failure_details(failure.name, failure.exception, i)
23
+ end
24
+ end
25
+
26
+ def failure_details(name, exception, i)
27
+ [ failure_header(name, exception, i),
28
+ expected_actual(exception),
29
+ backtrace(exception.backtrace),
30
+ "" ]
31
+ end
32
+
33
+ def failure_header(name, exception, i)
34
+ "F#{i+1}: #{exception.message} - #{name.inspect}"
35
+ end
36
+
37
+ def expected_actual(exception)
38
+ [" exp: #{exception.expected.inspect}",
39
+ " got: #{exception.actual.inspect}"
40
+ ].join("\n")
41
+ end
42
+
43
+ def exceptions
44
+ results.exceptions.each_with_index.flat_map do |exception, i|
45
+ exception_details(exception.name, exception.exception, i)
46
+ end
47
+ end
48
+
49
+ def exception_details(name, exception, i)
50
+ [ exception_header(name, exception, i),
51
+ backtrace(exception.backtrace),
52
+ "" ]
53
+ end
54
+
55
+ def exception_header(name, exception, i)
56
+ "E#{i+1}: #{exception.inspect} - #{name.inspect}"
57
+ end
58
+
59
+
60
+ def backtrace(trace)
61
+ filter_backtrace(trace).map { |line| "* #{line}" }.join("\n")
62
+ end
63
+
64
+ def conclusion
65
+ [ "#{elapsed}: #{passed}, #{failed}, #{raised}." ]
66
+ end
67
+
68
+ def elapsed
69
+ "Ran %d tests in %.6fs" % [results.count, results.elapsed]
70
+ end
71
+ def passed; "#{results.successes.count} passed" end
72
+ def failed; "#{results.failures.count} failed" end
73
+ def raised; "#{results.exceptions.count} raised" end
74
+
75
+ def filter_backtrace(backtrace)
76
+ backtrace.reject do |line|
77
+ line =~ BACKTRACE_FILTER
78
+ end.map do |line|
79
+ line.gsub("#{Dir.pwd}/", "")
80
+ end
81
+ end
82
+
83
+ BACKTRACE_FILTER =
84
+ /lib\/tst\.rb|bin\/tst|\/(ruby|jruby|rbx)[-\/]([0-9\.])+|Rakefile/
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,29 @@
1
+ module Tst
2
+ module Reporters
3
+ class Pretty < Plain
4
+ def color(n, s) "\e[#{n}m#{s}\e[0m" end
5
+
6
+ def black(s) color(30, s) end
7
+ def red(s) color(31, s) end
8
+ def green(s) color(32, s) end
9
+ def yellow(s) color(33, s) end
10
+ def blue(s) color(34, s) end
11
+ def magenta(s) color(35, s) end
12
+ def cyan(s) color(36, s) end
13
+ def white(s) color(37, s) end
14
+
15
+ def success(result) io.print green('.') end
16
+ def failure(result) io.print red('F') end
17
+ def exception(result) io.print yellow('E') end
18
+
19
+ def failure_header(*) red(super) end
20
+ def expected_actual(*) white(super) end
21
+ def backtrace(*) cyan(super) end
22
+ def exception_header(*) yellow(super) end
23
+
24
+ def passed; green(super) end
25
+ def failed; red(super) end
26
+ def raised; yellow(super) end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ require 'tst/reporters/plain'
2
+ require 'tst/reporters/pretty'
3
+
4
+ # ### Reporters ###
5
+ #
6
+ # Reporters are actually very simple.
7
+ #
8
+ # A reporter is just any object that responds to the following
9
+ # methods: `success`, `failure`, `exception`, `summarize`.
10
+ #
11
+ # `success`, `failure`, and `exception` get called as the tests
12
+ # are running - for real-time reporting.
13
+ #
14
+ # When called, they receive a Result instance as an argument, so
15
+ # you could list the name of each test after it ran.
16
+ #
17
+ # After all the tests run, the `summarize` method gets called with
18
+ # a Results (note the plural) object, which has information about
19
+ # the the tests that were run.
20
+ #
21
+ # Take a look at 'lib/tst.rb' for details about Result and Results.
22
+ #
23
+ # And take a look at the "plain" and "pretty" reporters in the
24
+ # 'lib/tst/reporters/' directory to see a couple of actual
25
+ # implementations.
data/lib/tst.rb ADDED
@@ -0,0 +1,267 @@
1
+ require 'tst/reporters'
2
+
3
+ # Tst
4
+ module Tst
5
+ VERSION = "0.0.1"
6
+
7
+ # ### Failure ###
8
+ #
9
+ # An error class that carries data about test failures.
10
+ class Failure < StandardError
11
+ attr_reader :message, :expected, :actual
12
+
13
+ # Takes the `message`, `expected`, and `actual` parameters
14
+ # and stuffs them into instance variables.
15
+ def initialize(message, expected, actual)
16
+ @message = message
17
+ @expected = expected
18
+ @actual = actual
19
+ super(message)
20
+ end
21
+ end
22
+
23
+ # ### Assertions ###
24
+ #
25
+ # Defines a few `assert` methods for use in tests.
26
+ #
27
+ # If you'd like to define your own assertion methods,
28
+ # open up the module and do so:
29
+ #
30
+ # module Tst::Assertions
31
+ # def assert_seven(actual)
32
+ # # return without raising to pass
33
+ # return if actual == 7
34
+ #
35
+ # # raise a Tst::Failure to fail
36
+ # raise Tst::Failure.new('Not 7', 7, actual)
37
+ # end
38
+ # end
39
+ #
40
+ # Because `Assertions` is included into `Test`, your
41
+ # new methods will be available in `tst` blocks.
42
+ module Assertions
43
+ # *assert*, the lie detector.
44
+ #
45
+ # It fails if `value` is `false` or `nil`. Succeeds otherwise.
46
+ def assert(value)
47
+ return if value
48
+ raise Failure.new("Failure: Truthiness", "not false or nil", value)
49
+ end
50
+
51
+ # *assert_equal*, the egalitarian.
52
+ #
53
+ # Fails unless it's arguments are equal (with `==`).
54
+ def assert_equal(expected, actual)
55
+ return if expected == actual
56
+ raise Failure.new("Equality Failure", expected, actual)
57
+ end
58
+
59
+ # *assert_raises*, the pessimist.
60
+ #
61
+ # Succeeds if it catches an error AND that error is
62
+ # a `kind_of?` the `expected` error.
63
+ #
64
+ # Fails otherwise.
65
+ def assert_raises(expected=StandardError)
66
+ begin
67
+ yield
68
+ rescue => actual
69
+ return if actual.kind_of?(expected)
70
+ raise Failure.new("Failure: Unexpected Exception", expected, actual)
71
+ end
72
+ raise Failure.new("Failure: No Exception", expected, "Nothing raised.")
73
+ end
74
+ end
75
+
76
+ # ### Test ###
77
+ #
78
+ # An instance of test is basically only responsible for providing
79
+ # a scope within which to run a block.
80
+ #
81
+ # It includes the Assertions module so that the assertions
82
+ # methods are available to you inside your tests.
83
+ class Test
84
+ include Assertions
85
+
86
+ attr_reader :name
87
+
88
+ def initialize(name, &block)
89
+ @name = name
90
+ @block = block
91
+ end
92
+
93
+ def run
94
+ instance_eval &@block
95
+ end
96
+ end
97
+
98
+ # Set up some constants to represent test outcomes.
99
+ SUCCEEDED = :success
100
+ FAILED = :failure
101
+ RAISED = :exception
102
+
103
+ # ### Result ###
104
+ #
105
+ # A tidy little place to store test result information.
106
+ # The values should be self-explanatory.
107
+ Result = Struct.new(:name, :status, :elapsed, :exception)
108
+
109
+
110
+ # ### Results ###
111
+ #
112
+ # A collection class that keeps track of test results.
113
+ class Results
114
+ attr_reader :count, :elapsed, :results
115
+
116
+ # It starts out with everything zeroed out.
117
+ def initialize
118
+ @count = 0
119
+ @elapsed = 0
120
+ @results = []
121
+ end
122
+
123
+ # Collects incoming `result`s and updates `count` and `elapsed`
124
+ # to reflect the new `result`.
125
+ def <<(result)
126
+ @count += 1
127
+ @elapsed += result.elapsed
128
+ results << result
129
+ end
130
+
131
+ # Accessors to pull out the subset of tests you need.
132
+ def successes; results.select { |r| r.status == SUCCEEDED } end
133
+ def failures; results.select { |r| r.status == FAILED } end
134
+ def exceptions; results.select { |r| r.status == RAISED } end
135
+ end
136
+
137
+ # ### Runner ###
138
+ #
139
+ # Collects and runs your tests.
140
+ class Runner
141
+ attr_reader :tests, :results, :reporter
142
+
143
+ # A new Runner accepts a Reporter instance, which it delegates
144
+ # to for displaying results. It defaults to the plain reporter.
145
+ #
146
+ # Look at 'lib/tst/reporters.rb' for more information on reporters.
147
+ def initialize(reporter=nil)
148
+ @tests = []
149
+ @results = Results.new
150
+ @reporter = reporter || Reporters::Plain.new
151
+ end
152
+
153
+ # Collects incoming tests.
154
+ def <<(test)
155
+ tests << test
156
+ end
157
+
158
+ # Runs all the tests it has and then hands control
159
+ # over to `reporter` to tell you about it.
160
+ def run!
161
+ tests.each { |test| run_test(test) }
162
+ reporter.summarize(results)
163
+ end
164
+
165
+ # The actual machinery for running a test.
166
+ def run_test(test)
167
+ start = Time.now
168
+ test.run
169
+ status = SUCCEEDED
170
+ rescue Failure => exception
171
+ status = FAILED
172
+ rescue StandardError => exception
173
+ status = RAISED
174
+ ensure
175
+ elapsed = Time.now - start
176
+ record(test.name, status, elapsed, exception)
177
+ end
178
+
179
+ # Adds a result to `results` and calls the `success`,
180
+ # `failure`, or `exception` method on `reporter` so
181
+ # it can show you your results as they come in.
182
+ def record(name, status, elapsed, exception=nil)
183
+ result = Result.new(name, status, elapsed, exception)
184
+ results << result
185
+ reporter.send(status, result)
186
+ end
187
+ end
188
+
189
+ # ### FileRunner ###
190
+ #
191
+ # A little wrapper class (around a Runner) to allow
192
+ # for easily running tests that are in files.
193
+ class FileRunner
194
+ attr_reader :runner
195
+
196
+ # Runs the tests in the files represented by `glob`.
197
+ #
198
+ # `glob`: is any string that `Dir.glob` knows how to handle.
199
+ #
200
+ # Accepts the following options:
201
+ # `:reporter`: any object that responds_to the reporter
202
+ # interface
203
+ # (see 'lib/tst/reporters.rb' for details)
204
+ #
205
+ # `:load_paths`: an Array of directories to be added to
206
+ # the load path before running the tests.
207
+ #
208
+ # Note that `run` makes a new Runner each time it's called,
209
+ # allowing it to be called several times on the same
210
+ # FileRunner instance while still doing the right thing.
211
+ def run(glob, options={})
212
+ @runner = Runner.new options[:reporter]
213
+ load_files(glob, options[:load_paths] || [])
214
+ runner.run!
215
+ end
216
+
217
+ # Collects incoming tests and hands them off to the `runner`.
218
+ def <<(test)
219
+ runner << test
220
+ end
221
+
222
+ # Modifies the load path if necessary and loads all the
223
+ # test files.
224
+ def load_files(glob, load_paths)
225
+ load_paths.each { |path| $:.unshift(path) }
226
+ Dir[glob].each { |file| load(file, true) }
227
+ end
228
+ end
229
+
230
+ # Module-level accessor to a FileRunner instance.
231
+ def self.file_runner
232
+ @file_runner ||= FileRunner.new
233
+ end
234
+
235
+ # Hands a test off to `file_runner`. This is called by
236
+ # the top-level `tst` method (i.e., the one you use to
237
+ # write your tests with).
238
+ def self.add_test(name, &block)
239
+ file_runner << Test.new(name, &block)
240
+ end
241
+
242
+ # The module-level API entry point.
243
+ #
244
+ # An example from Tst's own Rakefile:
245
+ #
246
+ # task :test do
247
+ # Tst.run 'test/*.rb',
248
+ # :load_paths => ['.', 'lib'],
249
+ # :reporter => Tst::Reporters::Pretty.new
250
+ # end
251
+ def self.run(glob, options={})
252
+ file_runner.run(glob, options)
253
+ end
254
+ end
255
+
256
+ # The `tst` method.
257
+ #
258
+ # Acts as proxy to `Tst.add_test`. This is the method you
259
+ # actually call as a consumer of the library.
260
+ #
261
+ # tst "the answer is correct" do
262
+ # answer = Answer.new
263
+ # assert_equal 42, answer
264
+ # end
265
+ def tst(name=nil, &block)
266
+ Tst.add_test(name, &block)
267
+ end
@@ -0,0 +1,61 @@
1
+ # :assert
2
+
3
+ tst "assert(nil) raises" do
4
+ assert_raises Tst::Failure do
5
+ assert nil
6
+ end
7
+ end
8
+
9
+ tst "assert(false) raises" do
10
+ assert_raises Tst::Failure do
11
+ assert false
12
+ end
13
+ end
14
+
15
+ tst "assert(true) does NOT raise" do
16
+ assert true
17
+ end
18
+
19
+ tst "assert(0) does NOT raise" do
20
+ assert 0
21
+ end
22
+
23
+ tst "assert('') does NOT raise" do
24
+ assert 'asdf'
25
+ end
26
+
27
+
28
+ # :assert_equal
29
+
30
+ tst "assert_equal raises if things are NOT ==" do
31
+ assert_raises Tst::Failure do
32
+ assert_equal "Arthur Dent", "Ford Prefect"
33
+ end
34
+ end
35
+
36
+ tst "assert_equal does NOT raise if things are ==" do
37
+ assert_equal 42, 42
38
+ end
39
+
40
+
41
+ # :assert_raises
42
+
43
+ tst "assert_raises raises if its block does NOT raise something" do
44
+ assert_raises Tst::Failure do
45
+ assert_raises { }
46
+ end
47
+ end
48
+
49
+ tst "assert_raises raises if its block doesn't raise the right thing" do
50
+ assert_raises Tst::Failure do
51
+ assert_raises(NameError) { raise RuntimeError }
52
+ end
53
+ end
54
+
55
+ tst "assert_raises does NOT raise if it gets the expected error" do
56
+ assert_raises(NameError) { raise NameError }
57
+ end
58
+
59
+ tst "assert_raises does NOT raise on expected error subclass" do
60
+ assert_raises(ArgumentError) { raise Class.new(ArgumentError) }
61
+ end
@@ -0,0 +1,3 @@
1
+ tst "one 'exceptional' test" do
2
+ raise "I'm a teapot"
3
+ end
@@ -0,0 +1,3 @@
1
+ tst "one failing test" do
2
+ assert false
3
+ end
@@ -0,0 +1,3 @@
1
+ tst "one passing test" do
2
+ assert true
3
+ end
@@ -0,0 +1,56 @@
1
+ require 'stringio'
2
+
3
+ def tst_runner_output(glob, expected)
4
+ tst "running #{glob}" do
5
+ io = StringIO.new('')
6
+ reporter = Tst::Reporters::Plain.new(io)
7
+ Tst.run glob, :reporter => reporter
8
+ assert_equal expected, io.string.gsub(/0\.\d+/, 'X.XXX')
9
+ end
10
+ end
11
+
12
+ tst_runner_output "test/fixtures/success.rb", <<-end
13
+ .
14
+ Ran 1 tests in X.XXXs: 1 passed, 0 failed, 0 raised.
15
+ end
16
+
17
+ tst_runner_output "test/fixtures/failure.rb", <<-end
18
+ F
19
+ F1: Failure: Truthiness - \"one failing test\"
20
+ exp: \"not false or nil\"
21
+ got: false
22
+ * test/fixtures/failure.rb:2:in `block in <top (required)>'
23
+ * test/plain_reporter.rb:7:in `block in tst_runner_output'
24
+
25
+ Ran 1 tests in X.XXXs: 0 passed, 1 failed, 0 raised.
26
+ end
27
+
28
+ tst_runner_output "test/fixtures/exception.rb", <<-end
29
+ E
30
+ E1: #<RuntimeError: I'm a teapot> - \"one 'exceptional' test\"
31
+ * test/fixtures/exception.rb:2:in `block in <top (required)>'
32
+ * test/plain_reporter.rb:7:in `block in tst_runner_output'
33
+
34
+ Ran 1 tests in X.XXXs: 0 passed, 0 failed, 1 raised.
35
+ end
36
+
37
+ tst_runner_output "test/fixtures/{exception,success,failure}.rb", <<-end
38
+ E.F
39
+ F1: Failure: Truthiness - \"one failing test\"
40
+ exp: \"not false or nil\"
41
+ got: false
42
+ * test/fixtures/failure.rb:2:in `block in <top (required)>'
43
+ * test/plain_reporter.rb:7:in `block in tst_runner_output'
44
+
45
+ E1: #<RuntimeError: I'm a teapot> - \"one 'exceptional' test\"
46
+ * test/fixtures/exception.rb:2:in `block in <top (required)>'
47
+ * test/plain_reporter.rb:7:in `block in tst_runner_output'
48
+
49
+ Ran 3 tests in X.XXXs: 1 passed, 1 failed, 1 raised.
50
+ end
51
+
52
+ tst_runner_output "test/fixtures/does_not_exist.rb", <<-end
53
+
54
+ Ran 0 tests in X.XXXs: 0 passed, 0 failed, 0 raised.
55
+ end
56
+
data/test/test.rb ADDED
@@ -0,0 +1,23 @@
1
+ # One nice thing is that each test gets a fresh scope,
2
+ # which helps mitigate state leakage...
3
+ tst "tests don't have access to top-level instance variables" do
4
+ @outside = "I'm outside"
5
+
6
+ Tst::Test.new "instance variable" do
7
+ @outside = "I'm inside, muahahaha!"
8
+ end.run
9
+
10
+ assert_equal "I'm outside", @outside
11
+ end
12
+
13
+ # ... but ruby block scope can be a bitch.
14
+ tst "for better or for worse, local variables are fair game" do
15
+ outside = "I'm outside"
16
+
17
+ Tst::Test.new "local variable" do
18
+ outside = "I'm inside, muahahaha!"
19
+ end.run
20
+
21
+ assert_equal "I'm inside, muahahaha!", outside
22
+ end
23
+
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tst
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Arun Srinivasan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-26 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A small testing library that tries not to do too much.
15
+ email: satchmorun@gmail.com
16
+ executables:
17
+ - tst
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - LICENSE
23
+ - Rakefile
24
+ - bin/tst
25
+ - lib/tst/reporters/plain.rb
26
+ - lib/tst/reporters/pretty.rb
27
+ - lib/tst/reporters.rb
28
+ - lib/tst.rb
29
+ - test/assertions.rb
30
+ - test/fixtures/exception.rb
31
+ - test/fixtures/failure.rb
32
+ - test/fixtures/success.rb
33
+ - test/plain_reporter.rb
34
+ - test/test.rb
35
+ homepage: http://github.com/satchmorun/tst
36
+ licenses:
37
+ - MIT
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 1.8.10
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: A small testing library that tries not to do too much.
60
+ test_files:
61
+ - test/assertions.rb
62
+ - test/fixtures/exception.rb
63
+ - test/fixtures/failure.rb
64
+ - test/fixtures/success.rb
65
+ - test/plain_reporter.rb
66
+ - test/test.rb