Automatic testing

The correct way to develop any piece of code is to use automatic testing. The advantages are:

  • The automated repetition of a large number of tests after every code refactoring and before any new versions are launched.
  • A silent documentation of the use of the code.
  • A documentation of the test coverage of your code: Did things work before a change or was a certain aspect never tested?

Note

Changes in the program and in particular in its structure which do not affect its functionality are called code refactoring.

We suggest developing tests in parallel to the code. Good design of tests is an art of its own and there is rarely an investment which guarantees such a good pay-off in development time savings as the investment in good tests.

Now we will go through the implementation of a simple algorithm with the automated testing methods in mind.

Testing the bisection algorithm

Let us examine automated testing for the bisection algorithm. With this algorithm, a zero of a real valued function is found. It is described section Exercise 4 in Chapter 7, Functions. An implementation of the algorithm can have the following form:

def bisect(f, a, b, tol=1.e-8):
    """
    Implementation of the bisection algorithm 
    f real valued function
    a,b interval boundaries (float) with the property 
    f(a) * f(b) <= 0
    tol tolerance (float)
    """
    if f(a) * f(b)> 0:
        raise ValueError("Incorrect initial interval [a, b]") 
    for i in range(100):
        c = (a + b) / 2.
        if f(a) * f(c) <= 0:
            b = c
        else:
            a = c
        if abs(a - b) < tol:
            return (a + b) / 2
    raise Exception(
          'No root found within the given tolerance {}'.format(tol))

We assume this to be stored in the bisection.py file. As the first test case, we test that the zero of the function f(x) = x is found:

def test_identity():
    result = bisect(lambda x: x, -1., 1.) 
    expected = 0.
    assert allclose(result, expected),'expected zero not found'

test_identity()

In this code, you meet the Python keyword assert for the first time. It raises AssertionError exception if its first argument returns the False value. Its optional second argument is a string with additional information. We use the function allclose in order to test for equality of floats.

Let us comment on some of the features of the test function. We use an assertion to make sure that an exception will be raised if the code does not behave as expected. We have to manually run the test in the test_identity() line.

There are many tools to automate this kind of call.

Let us now set up a test that checks if bisect raises an exception when the function has the same sign on both ends of the interval. For now, we will suppose that the exception raised is a ValueError exception. In the following example, we will check the initial interval [a,b]. For the bisection algorithm it should fulfill a sign condition:

def test_badinput():
    try:
        bisect(lambda x: x,0.5,1)
    except ValueError:
        pass
    else:
        raise AssertionError()

test_badinput()

In this case, an AssertionError is raised if the exception is not of the ValueError type . There are tools to simplify the preceding construction to check that an exception is raised.

Another useful test is the edge case test. Here we test arguments or user input, which is likely to create mathematically undefined situations or states of the program not foreseen by the programmer. For instance, what happens if both bounds are equal? What happens if a > b?

def test_equal_boundaries():
    result = bisect(lambda x: x, 0., 0.)
    expected = 0.
    assert allclose(result, expected), 
                   'test equal interval bounds failed'

def test_reverse_boundaries():
    result = bisect(lambda x: x, 1., -1.)
    expected = 0.
    assert allclose(result, expected),
                 'test reverse interval bounds failed'
 
test_equal_boundaries()
test_reverse_boundaries()
..................Content has been hidden....................

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