Unit Testing

Unit testing is a postdebugging testing technique that lets you try bits of your program in order to verify that they work as expected. Some programmers use unit testing habitually in addition to or even instead of interactive debugging; other programmers use it rarely or never. Entire books have been written on the techniques and methodologies of unit testing, and I will only cover its fundamentals here.

The basic idea of unit testing is that you can write a number of “assertions” stating that certain results should be obtained as the consequence of certain actions. For example, you might assert that the return value of a specific method should be 100, that it should be a Boolean, or that it should be an instance of a specific class. If, when the test is run, the assertion proves to be correct, it passes the test; if it is incorrect, the test fails.

Here’s an example, which will fail if the getVal method of the object, t, returns any value other than 100:

assert_equal(100, t.getVal)

But you can’t just pepper your code with assertions of this sort. There are precise rules to the game. First you have to require the test/unit file. Then you need to derive a test class from the TestCase class, which is found in the Unit module, which is itself in the Test module:

class MyTest < Test::Unit::TestCase

Inside this class you can write one or more methods, each of which constitutes a test containing one or more assertions. The method names must begin with test (so methods called test1 or testMyProgram are okay, but a method called myTestMethod isn’t). The following method contains a test that makes the single assertion that the return value of TestClass.new(100).getVal is 1,000:

def test2
    assert_equal(1000,TestClass.new(100).getVal)
end

And here is a complete (albeit simple) test suite in which I have defined a TestCase class called MyTest that tests the class, TestClass. Here (with a little imagination!), TestClass may be taken to represent a whole program that I want to test:

test1.rb

require 'test/unit'

class TestClass
    def initialize( aVal )
        @val = aVal * 10
    end

    def getVal
        return @val
    end
end

class MyTest < Test::Unit::TestCase
    def test1
        t = TestClass.new(10)
        assert_equal(100, t.getVal)
        assert_equal(101, t.getVal)
        assert(100 != t.getVal)
    end

    def test2
        assert_equal(1000,TestClass.new(100).getVal)
    end
end

This test suite contains two tests: test1 (which contains three assertions) and test2 (which contains one). To run the tests, you just need to run the program; you don’t have to create an instance of MyClass. You will see a report of the results that states there were two tests, three assertions, and one failure:

1) Failure:
test1(MyTest) [C:/bookofruby/ch18/test1.rb:19]:
<101> expected but was
<100>.

2 tests, 3 assertions, 1 failures, 0 errors

In fact, I made four assertions. However, assertions following a failure are not evaluated in a given test. In test1, this assertion fails:

assert_equal(101, t.getVal)

Having failed, the next assertion is skipped. If I now correct this failed assertion (asserting 100 instead of 101), this next assertion will also be tested:

assert(100 != t.getVal)

But this too fails. This time, the report states that four assertions have been evaluated with one failure:

2 tests, 4 assertions, 1 failures, 0 errors, 0 skips

Of course, in a real-life situation, you should aim to write correct assertions, and when any failures are reported, it should be the failing code that is rewritten—not the assertion!

For a slightly more complex example of testing, see the test2.rb program (which requires a file called buggy.rb). This is a small adventure game that includes the following test methods:

test2.rb

def test1
    @game.treasures.each{ |t|
        assert(t.value < 2000, "FAIL: #{t} t.value = #{t.value}" )
    }
end

def test2
    assert_kind_of( TestMod::Adventure::Map, @game.map)
    assert_kind_of( Array, @game.map)
end

Here the first method, test1, performs an assert test on an array of objects passed into a block, and it fails when a value attribute is not less than 2,000. The second method, test2, tests the class types of two objects using the assert_kind_of method. The second test in this method fails when @game.map is found to be of the type TestMod::Adventure::Map rather than Array as is asserted.

The code also contains two more methods named setup and teardown. When defined, methods with these names will be run before and after each test method. In other words, in test2.rb, the following methods will run in this order:

1. setup

2. test1

3. teardown

4. setup

5. test2

6. teardown

This gives you the opportunity of reinitializing any variables to specific values prior to running each test or, as in this case, re-creating objects to ensure that they are in a known state as in the following example:

def setup
    @game = TestMod::Adventure.new
end

def teardown
    @game.endgame
end
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.126.56