Test::Unit is included in the Ruby Standard Library. It is also used by default in Rails apps.
Test::Unit is RDoc'd nicely and is available online.
An example Test::Unit test outside of Rails looks like this:
require 'test/unit' class CalculatorTest < Test::Unit::TestCase def test_add_two_numbers_for_the_sum calculator = Calculator.new assert_equal 4, calculator.sum(2, 2) end end
TestCase does the nitty-gritty of actually running an individual test and collecting its results into a Test::Unit::TestResult object.
You subclass TestCase and write regular Ruby methods whose names are prefixed with test_.
When you run the file, Test::Unit does a little magic based on the test_ convention and executes those methods. Notice that we did not name the method “test_calculator_add_two_numbers_for_the_sum”. We can assume that we are dealing with a Calculator because we are inside the CalculatorTest case.
Test methods are run in isolation of other tests. Like other testing frameworks, in most cases, “the world is set back to its original state” after each test.
Regardless of the testing framework you choose, the pattern will be the same:
In the Calculator example used above, the setup portion is:
calculator = Calculator.new
We then exercise the method under test:
calculator.sum(2, 2)
We then verify that the implementation works correctly by asserting expectations with a Test::Unit assertion:
assert_equal
There is nothing to teardown in this example. If your test is doing something “out of the ordinary”, such as creating files or creating network or database connections, you will have to use the teardown method to clean up after yourself:
def teardown # delete files, close network or database connections, etc. end
For more about Four-Phase Test, see the page on the subject at XUnit Test Patterns.
assert_equal may be more common in usage that assert:
assert_equal "Rubyist", developer.job_title
We can assert nil or not nil:
assert_not_nil developer.salary
We can even assert that Exceptions are raised by putting our exercise inside a block that is yielded to from assert_raise:
calculator.remove_battery! assert_raise NoPowerError do calculator.sum(2, 2) end
Rails provides ActiveSupport::TestCase, ActionController::TestCase, and ActionController::IntegrationTest, which work just like Test::Unit in Ruby, with some extra sugar.
Rails automatically creates test files for you when you use its generators, such as when creating controllers and models. Here is an example directory structure:
app/
controllers/
calculator_controller.rb
models/
calculator.rb
test/
functional/
calculator_controller_test.rb
unit/
calculator_test.rb
You can see that functional tests are associated with controllers and unit tests are associated with models.
ActiveSupport also makes the syntax of test methods slightly nicer:
class CalculatorTest < ActiveSupport::TestCase test "add two numbers for the sum" do calculator = Calculator.new assert_equal 4, calculator.sum(2, 2) end end
Notice we have dropped the “define a Ruby method prefixed with test_” approach in favor of a “test method that takes a test name as a string and test contents inside a block” approach.
This is a really nice way of avoiding those ugly underscores in Test::Unit method names, which are often sentence-length.
Discussion
Should we be covering minitest here? Or under its own heading?