From Wikibooks, open books for an open world
Unit testing is a great way to catch errors early in the development process, if you dedicate time to writing appropriate and useful tests. As in other languages, Ruby provides a framework in its standard library for setting up, organizing, and running tests called Test::Unit.
There are other very popular testing frameworks, rspec and cucumber come to mind.
Specifically, Test::Unit provides three basic functionalities:
First create a new class.
# File: simple_number.rb class SimpleNumber def initialize(num) raise unless num.is_a?(Numeric) @x = num end def add(y) @x + y end def multiply(y) @x * y end end
Let's start with an example to test the SimpleNumber class.
# File: tc_simple_number.rb require_relative "simple_number" require "test/unit" class TestSimpleNumber < Test::Unit::TestCase def test_simple assert_equal(4, SimpleNumber.new(2).add(2) ) assert_equal(6, SimpleNumber.new(2).multiply(3) ) end end
Which produces
>> ruby tc_simple_number.rb Loaded suite tc_simple_number Started . Finished in 0.002695 seconds. 1 tests, 2 assertions, 0 failures, 0 errors
So what happened here? We defined a class TestSimpleNumber which inherited from Test::Unit::TestCase. In TestSimpleNumber we defined a member function called test_simple. That member function contains a number of simple assertions which exercise my class. When we run that class (note I haven't put any sort of wrapper code around it -- it's just a class definition), the tests are automatically run, and we're informed that we've run 1 test and 2 assertions.
Let's try a more complicated example.
# File: tc_simple_number2.rb require_relative "simple_number" require "test/unit" class TestSimpleNumber < Test::Unit::TestCase def test_simple assert_equal(4, SimpleNumber.new(2).add(2) ) assert_equal(4, SimpleNumber.new(2).multiply(2) ) end def test_typecheck assert_raise( RuntimeError ) { SimpleNumber.new('a') } end def test_failure assert_equal(3, SimpleNumber.new(2).add(2), "Adding doesn't work" ) end end
>> ruby tc_simple_number2.rb Loaded suite tc_simple_number2 Started F.. Finished in 0.038617 seconds. 1) Failure: test_failure(TestSimpleNumber) [tc_simple_number2.rb:16]: Adding doesn't work. <3> expected but was <4>. 3 tests, 4 assertions, 1 failures, 0 errors
Now there are three tests (three member functions) in the class. The function test_typecheck uses assert_raise to check for an exception. The function test_failure is set up to fail, which the Ruby output happily points out, not only telling us which test failed, but how it failed (expected <3> but was <4>). On this assertion, we've also added an final parameters which is a custom error message. It's strictly optional but can be helpful for debugging. All of the assertions include their own error messages which are usually sufficient for simple debugging.
Test::Unit provides a rich set of assertions, which are documented thoroughly at Ruby-Doc. Here's a brief synopsis (assertions and their negative are grouped together. The text description is usually for the first one listed -- the names should make some logical sense):
assert( boolean, [message] ) True if boolean assert_equal( expected, actual, [message] )Tests for a particular unit of code are grouped together into a test case, which is a subclass of Test::Unit::TestCase. Assertions are gathered in tests, member functions for the test case whose names start with test_. When the test case is executed or required, Test::Unit will iterate through all of the tests (finding all of the member functions which start with test_ using reflection) in the test case, and provide the appropriate feedback.
Test case classes can be gathered together into test suites which are Ruby files which require other test cases:
# File: ts_all_the_tests.rb require 'test/unit' require 'test_one' require 'test_two' require 'test_three'
In this way, related test cases can be naturally grouped. Further, test suites can contain other test suites, allowing the construction of a hierarchy of tests.
This structure provides relatively fine-grained control over testing. Individual tests can be run from a test case (see below), a full test case can be run stand-alone, a test suite containing multiple cases can be run, or a suite of suites can run, spanning many test cases.
The author of Test::Unit, Nathaniel Talbott, suggests starting the names of test cases with tc_ and the names of test suites with ts_
It's possible to run just one (or more) tests out of a full test case:
>> ruby -w tc_simple_number2.rb --name test_typecheck Loaded suite tc_simpleNumber2 Started . Finished in 0.003401 seconds. 1 tests, 1 assertions, 0 failures, 0 errors
It is also possible to run all tests whose names match a given pattern:
>> ruby -w tc_simple_number2.rb --name /test_type.*/ Loaded suite tc_simpleNumber2 Started . Finished in 0.003401 seconds. 1 tests, 1 assertions, 0 failures, 0 errors
There are many cases where a small bit of code needs to be run before and/or after each test. Test::Unit provides the setup and teardown member functions, which are run before and after every test (member function).
# File: tc_simple_number3.rb require "./simple_number" require "test/unit" class TestSimpleNumber < Test::Unit::TestCase def setup @num = SimpleNumber.new(2) end def teardown ## Nothing really end def test_simple assert_equal(4, @num.add(2) ) end def test_simple2 assert_equal(4, @num.multiply(2) ) end end
>> ruby tc_simple_number3.rb Loaded suite tc_simple_number3 Started .. Finished in 0.00517 seconds. 2 tests, 2 assertions, 0 failures, 0 errors
Implement a class with a public method that can solve the following problem: A user has a rubber bicycle tire of an arbitrary circumference. When one side is cut, and the bike tire is stretched into a line, it is measured at an arbitrary length. From this length, determine the radius of the bike tire originally.
After this, write a test case to test the class. It should include at least 2 methods to test.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4