= Protest, the simplicity rebel test framework
require "protest"
Protest.context("A user") do
setup do
@user = User.new(:name => "John Doe", :email => "john@example.org")
end
test "has a name" do
assert_equal "John Doe", @user.name
end
test "has an email" do
assert_equal "john@example.org", @user.email
end
end
Protest is a small, simple, and easy-to-extend testing framework for ruby. It
was written as a replacement for Test::Unit, given how awful its code is, and
how difficult it is to extend in order to add new features.
I believe in minimalistic software, which is easily understood, easy to test,
and specially, easy to extend for third parties. That's where I'm aiming with
Protest.
== Get it
gem install protest
Or
rip install git://github.com/foca/protest.git v0.2.3
== Setup and teardown
If you need to run code before or after each test, declare a +setup+ or
+teardown+ block (respectively.)
Protest.context("A user") do
setup do # this runs before each test
@user = User.create(:name => "John")
end
teardown do # this runs after each test
@user.destroy
end
end
+setup+ and +teardown+ blocks are evaluated in the same context as your test,
which means any instance variables defined in any of them are available in the
rest.
You can also use +global_setup+ and +global_teardown+ to run code only once per
test case. +global_setup+ blocks will run once before the first test is run, and
+global_teardown+ will run after all the tests have been run.
These methods, however, are dangerous, and should be used with caution, as
they might introduce dependencies between your tests if you don't write
your tests properly. Make sure that any state modified by code run in a
+global_setup+ or +global_teardown+ isn't changed in any of your tests.
Also, you should be aware that the code of +global_setup+ and +global_teardown+
blocks isn't evaluated in the same context as your tests and normal
+setup+/+teardown+ blocks are, so you can't share instance variables between
them.
== Nested contexts
Break down your test into logical chunks with nested contexts:
Protest.context("A user") do
setup do
@user = User.make
end
context "when validating" do
test "validates name" do
@user.name = nil
assert !@user.valid?
end
# etc, etc
end
context "doing something else" do
# your get the idea
end
end
Any +setup+ or +teardown+ blocks you defined in a context will run in that
context and in any other context nested in it.
== Pending tests
There are two ways of marking a test as pending. You can declare a test with no
body:
Protest.context("Some tests") do
test "this test will be marked as pending"
test "this tests is also pending"
test "this test isn't pending" do
assert true
end
end
Or you can call the +pending+ method from inside your test.
Protest.context("Some tests") do
test "this test is pending" do
pending "oops, this doesn't work"
assert false
end
end
== Custom assertions
By default Protest bundles all the assertions defined in Test::Unit (it
literally requires them), so check its documentation for all the goodness.
If you want to add assertions, just define methods that rely on +assert+ or
+assert_block+. The former takes a boolean and an optional error message as
arguments, while the latter takes an optional error message as an argument and
a block. The assertions is considered to fail if the block evaluates to neither
+false+ nor +nil+.
For example:
module AwesomenessAssertions
def assert_awesomeness(object)
assert object.awesome?, "#{object.inspect} is not awesome enough"
end
end
class Protest::TestCase
include AwesomenessAssertions
end
You could also define rspec-like matchers if you like that style. See
matchers.rb in the examples directory for an example.
== Reports
Protest can report the output of a test suite in many ways. The library ships
with a :progress report, and a :documentation report,
:progress being the default.
=== Progress report
This is the default option, but you can force this by calling
Protest.report_with(:progress).
The progress report will output the "classic" Test::Unit output of periods for
passing tests, "F" for failing assertions, "E" for unrescued exceptions, and
"P" for pending tests, in full color.
=== Documentation report
Use this report by calling Protest.report_with(:documentation)
For each testcase in your suite, this will output the description of the test
case (whatever you provide TestCase.context), followed by the name of each test
in that context, one per line. For example:
Protest.context "A user" do
test "has a name"
test "has an email"
context "validations" do
test "ensure the email can't be blank"
end
end
Will output, when run with the :documentation report:
A user
- has a name (Not Yet Implemented)
- has an email (Not Yet Implemented)
A user validations
- ensure the email can't be blank (Not Yet Implemented)
(The 'Not Yet Implemented' messages are because the tests have no body. See
"Pending tests", above.)
This is similar to the specdoc runner in rspec[http://rspec.info].
=== Defining your own reports
This is really, really easy. All you need to do is subclass Report, and
register your subclass by calling +Protest.add_report+. See the
documentation for details, or take a look at the source code for
Protest::Reports::Progress and Protest::Reports::Documentation.
== Legal
Author:: Nicolás Sanguinetti — http://nicolassanguinetti.info
License:: MIT (see bundled LICENSE file for more info)