tst 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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