Testicles, the ballsy test framework

require "testicles"

class UserTest < Testicles::TestCase
  setup do
    @user = User.new(:name => "John Doe", :email => "[email protected]")
  end

  test "has a name" do
    assert @user.name == "John Doe"
  end

  test "has an email" do
    assert @user.email == "[email protected]"
  end
end

Testicles 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 Testicles.

Assertions

You probably wonder why the example doesn’t have any assertion other than assert. Why not assert_equal, right? Well, the idea is to keep it slim. If you want to use more assertions, you’re free to define your own. Also, since it uses the same assertion API as Test::Unit, you can just require its assertions:

require "testicles"
require "test/unit/assertions"

class UserTest < Testicles::TestCase
  include Test::Unit::Assertions

  # now you can use all of Test::Unit assertions. For free.
end

You can even define rspec-like matchers if you want.

Setup and teardown

If you need to run code before or after each test, declare a setup or teardown block (respectively.)

class UserTest < Testicles::TestCase
  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:

class UserTest < Testicles::TestCase
  setup do
    @user = User.make
  end

  context "validations" do
    test "validates name" do
      @user.name = nil
      assert !@user.valid?
    end

    # etc, etc
  end

  context "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:

class SomeTest < Testicles::TestCase
  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.

class SomeTest < Testicles::TestCase
  test "this test is pending" do
    pending "oops, this doesn't work"
    assert false
  end
end

(Best|Worst) name ever

I personally believe the name is full of win. But I understand it’s not the most marketable name :)

If you hate it, suggest a better name. Or just fork it and name your version different, it’s open source, after all.

Author

Nicolás Sanguinetti — nicolassanguinetti.info

License

MIT (see bundled LICENSE file for more info)