Movatterモバイル変換


[0]ホーム

URL:


Jump to content
WikibooksThe Free Textbook Project
Search

Ruby Programming/Unit testing

From Wikibooks, open books for an open world
<Ruby Programming

← Exceptions| RubyDoc →


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:

  1. A way to define basic pass/fail tests.
  2. A way to gather related tests together and run them as a group.
  3. Tools for running single tests or whole groups of tests.

A Simple Introduction

[edit |edit source]

First create a new class.

# File:  simple_number.rbclassSimpleNumberdefinitialize(num)raiseunlessnum.is_a?(Numeric)@x=numenddefadd(y)@x+yenddefmultiply(y)@x*yendend

Let's start with an example to test the SimpleNumber class.

# File:  tc_simple_number.rbrequire_relative"simple_number"require"test/unit"classTestSimpleNumber<Test::Unit::TestCasedeftest_simpleassert_equal(4,SimpleNumber.new(2).add(2))assert_equal(6,SimpleNumber.new(2).multiply(3))endend

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 classTestSimpleNumber which inherited fromTest::Unit::TestCase. InTestSimpleNumber we defined a member function calledtest_simple. That member function contains a number of simpleassertions 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.rbrequire_relative"simple_number"require"test/unit"classTestSimpleNumber<Test::Unit::TestCasedeftest_simpleassert_equal(4,SimpleNumber.new(2).add(2))assert_equal(4,SimpleNumber.new(2).multiply(2))enddeftest_typecheckassert_raise(RuntimeError){SimpleNumber.new('a')}enddeftest_failureassert_equal(3,SimpleNumber.new(2).add(2),"Adding doesn't work")endend
>> ruby tc_simple_number2.rbLoaded suite tc_simple_number2StartedF..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 functiontest_typecheck usesassert_raise to check for an exception. The functiontest_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.

Available Assertions

[edit |edit source]

Test::Unit provides a rich set of assertions, which are documented thoroughly atRuby-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 ifboolean
assert_equal( expected, actual, [message] )
assert_not_equal( expected, actual, [message] )
True ifexpected == actual
assert_match( pattern, string, [message] )
assert_no_match( pattern, string, [message] )
True ifstring =~ pattern
assert_nil( object, [message] )
assert_not_nil( object, [message] )
True ifobject == nil
assert_in_delta( expected_float, actual_float, delta, [message] )True if(actual_float - expected_float).abs <= delta
assert_instance_of( class, object, [message] )True ifobject.class == class
assert_kind_of( class, object, [message] )True ifobject.kind_of?(class)
assert_same( expected, actual, [message])
assert_not_same( expected, actual, [message] )
True ifactual.equal?( expected ).
assert_raise( Exception,... ) {block}
assert_nothing_raised( Exception,...) {block}
True if the block raises (or doesn't) one of the listed exceptions.
assert_throws( expected_symbol, [message] ) {block}
assert_nothing_thrown( [message] ) {block}
True if the block throws (or doesn't) the expected_symbol.
assert_respond_to( object, method, [message] )True if the object can respond to the given method.
assert_send( send_array, [message] )True if the method sent to the object with the given arguments return true.
assert_operator( object1, operator, object2, [message] )Compares the two objects with the given operator, passes iftrue

Structuring and Organizing Tests

[edit |edit source]

Tests for a particular unit of code are grouped together into atest case, which is a subclass of Test::Unit::TestCase. Assertions are gathered intests, member functions for the test case whose names start withtest_. 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 withtest_ using reflection) in the test case, and provide the appropriate feedback.

Test case classes can be gathered together intotest suites which are Ruby files which require other test cases:

# File: ts_all_the_tests.rbrequire'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.

Naming Conventions

[edit |edit source]

The author of Test::Unit, Nathaniel Talbott, suggests starting the names of test cases withtc_ and the names of test suites withts_

Running Specific Tests

[edit |edit source]

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_simpleNumber2Started.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_simpleNumber2Started.Finished in 0.003401 seconds.1 tests, 1 assertions, 0 failures, 0 errors

Setup and Teardown

[edit |edit source]

There are many cases where a small bit of code needs to be run before and/or after each test. Test::Unit provides thesetup andteardown member functions, which are run before and after every test (member function).

# File:  tc_simple_number3.rbrequire"./simple_number"require"test/unit"classTestSimpleNumber<Test::Unit::TestCasedefsetup@num=SimpleNumber.new(2)enddefteardown## Nothing reallyenddeftest_simpleassert_equal(4,@num.add(2))enddeftest_simple2assert_equal(4,@num.multiply(2))endend
>> ruby tc_simple_number3.rbLoaded suite tc_simple_number3Started..Finished in 0.00517 seconds.2 tests, 2 assertions, 0 failures, 0 errors

Exercises

[edit |edit source]

Radius of a bike tire

[edit |edit source]

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.

External Links

[edit |edit source]
Retrieved from "https://en.wikibooks.org/w/index.php?title=Ruby_Programming/Unit_testing&oldid=3678151"
Category:

[8]ページ先頭

©2009-2025 Movatter.jp