Tst
A small testing library that tries not to do too much.
You get a top-level tst
method, a few assert
methods, and readable test
output.
Installation
Standard issue:
gem install tst
Usage
The tst
method is available on the top-level, takes an optional name and
a required block, like so:
tst "Array-indexing is not 1-based" do
arr = [1, 2, 3, 4, 5]
assert arr[1] != 1
end
If the block runs to completion without raising anything, it passes.
If one of assertion methods (see below) raises, it counts as a failure.
If the block raises for any other reason, it's an exception.
Assertions
Within a tst
block, you have access to 3 assert methods: assert
,
assert_equal
, and assert_raises
. They do what you'd expect.
If those don't cover your needs, just write your own:
module Tst
# The assertions live in the Assertions module and are available
# to the `tst` blocks.
module Assertions
# No naming or argument rules, it's just a method.
# Define it how you please.
def sums_to_42(*args)
actual = args.inject(0) { |sum, arg| sum + arg }
# return without raising to pass
return if actual == 42
# Tst::Failure is a subclass of StandardError that takes
# 3 arguments: Failure.new(message, expected, actual)
#
# message - a brief description of the failure
# expected - the expected value
# actual - the actual value
raise Tst::Failure.new("#{args} doesn't sum to 42", 42, actual)
end
end
end
# Now just use your custom assertion in a test
tst "some array sums to 42" do
sums_to_42(40, 1, 1) # This will pass just fine
sums_to_42(40, 1, 2) # This will fail
end
Running that test will yield output like:
$ tst 'some_array.rb'
F
F1: [40, 1, 2] doesn't sum to 42 - "some array sums to 42"
exp: 42
act: 43
* some_array.rb:<lineno>:in `block in <top (required)>'
Running tests
Tst comes with a command-line runner, named "tst".
$ tst
Usage: tst [options] files
-h, --help Displays this message
-I, --libdir A directory to add to the load path
(can be used multiple times: "-I lib -I test"
-r, --reporter Specify a reporter to use. "pretty" or "plain".
Defaults to "plain"
files Specifies which test files to run. Is passed through to
Dir.glob, so anything it accepts is valid. NOTE: use
quotes to make sure nothing gets swallowed.
You can also run tests programmatically. Here's a sample rake task, for instance:
require 'tst'
desc 'Run the tests'
task :test do
Tst.run 'test/*.rb',
:load_paths => ['.', 'lib'], # same as -I above
:reporter => Tst::Reporters::Pretty.new # same as -r above
end
Meanderings
Why aren't there setup and teardown methods?
Having setup and teardown methods makes it harder to look at one test and see what's happening, as it's easy for relevant stuff to be scrolled off the screen, or even hidden in a superclass. And the names are totally generic.
And we're in ruby! You can get what you need by just writing a method with a name that encapsulates what you need. Take a look at "test/plain_reporter.rb" for an example.
Lightness
Tst tries to avoid weight, in various forms:
Weighty Structure
Tst has no grouping - TestCases, Context blocks, etc, because we're in ruby, where such ceremony is unnecessary. It is not required to make the testing tools work, and I'm not sure it yields much benefit. We already put our test code into files with names. Is further taxonomy really necessary?
I believe the lack of ceremony is easier to understand, learn, use, and is nicer to look at.
Conceptual weight
This may just be a personal preference thing, but I don't understand the appeal of the 'english-y' DSL's.
# Test tool concepts
# ------------------
# Compare this...
tst "a new Thing is not empty" do # :tst
thing = Thing.new
assert !thing.empty? # :assert
end
# ... to this (RSpec):
describe Thing do # :describe
let(:thing) { Thing.new } # :let
it "should be empty" do # :it
thing.should be_empty # :should
end # magic :be_* method
# how :thing gets in scope
end
# or with even more magic:
describe Thing do # :describe
it { should be_empty } # :it
end # magic Thing initialization
# implicit :subject
# :should (w/ implicit :subject)
# :be_*
Some argue that the RSpec examples read well, but look at how much you need to know to understand what's going on.
Don't get me wrong. RSpec is a pretty cool implementation of an interesting idea, but I'm not convinced that it's worth the conceptual overhead when you consider what you get.
Code weight
Just looking in the 'lib' directories:
- test/unit: 6313
- rspec: 5460
- minitest: 1210
- tst: 190
The core of tst's testing engine is really just 91 lines (see 'tst.rb'). The other 99 is IO/reporting code.