nose testing

I have a simple class - is has 3 numbers in a list [1,2,3].

This is the initial test for the class

from MySimpleModule import MySimpleModule

cls=None

def test_000_init():
    global cls
    cls=MySimpleModule()
    assert cls


def test_010_isin_good():
    global cls
    assert cls.isInData(1) == True


def test_020_isin_bad():
    global cls
    assert cls.isInData(99) == False

def test_030_isin_bad_data():
    global cls
    try:
        cls.isInData("a")
    except Exception as err:
        assert type(err) == ValueError

This is OK - but we have not really tested everything.

We could go to "Manual" Mode i.e.

def test_010_1_good():
    global cls
    assert cls.isInData(1) == True

def test_010_2_good():
    global cls
    assert cls.isInData(2) == True

def test_010_3_good():
    global cls
    assert cls.isInData(3) == True

More Simplified

But this is a real pain for wider testing scenarios.

We could use a system like this

def test_gen_lots():
    global cls
    for n in range(1,4):
        assert check_this(n) == True
    for n in range(4, 8):
        assert check_this(n) == False

def check_this(n):
    global cls
    return cls.isInData(n)

However the Nose output only shows

test_001.test_000_init ... ok
test_001.test_010_isin_good ... ok
test_001.test_020_isin_bad ... ok
test_001.test_030_isin_bad_data ... ok
test_001.test_gen_lots ... ok

Why do I not like this ? Well the test_get_lots() actually has 6 tests (3 Positive, 3 Negative) - I would like to see all these tests being shown. But how .... ?

Class Generator for Testing

The previous testing mechanism was includive, but we could not see each individual test.

To accomplish this we create a Class with a simple call method.

class report(object):
    def __call__(self, ExpectedResult,desc,value,n):
        self.description = 'Test {}:{}'.format(desc,value)
        assert ExpectedResult == value , '{} {}'.format(ExpectedResult, value)

This definition could be expanded to cope with test functions which need more than 1 parameter - but for the moment lets keep it simple.

The method checks the result with a Description (for the nose output.).

Make the Tests

Using nearly the same structure as test_gen_lots we write our test function as

def test_gen_better_lots():
    global cls
    for n in range(1, 4):
        yield report(), True,  "Test {}".format(n), check_this(n),n
    for n in range(4, 8):
        yield report(), False, "Test {}".format(n), check_this(n),n

def check_this(n):
    global cls
    return cls.isInData(n)

The check_this function is the same - but has been included for clarity.

What is the output ? (I have ommitted the other tests)

test_001.test_gen_better_lots(True, 'Test 1', True, 1) ... ok
test_001.test_gen_better_lots(True, 'Test 2', True, 2) ... ok
test_001.test_gen_better_lots(True, 'Test 3', True, 3) ... ok
test_001.test_gen_better_lots(False, 'Test 4', False, 4) ... ok
test_001.test_gen_better_lots(False, 'Test 5', False, 5) ... ok
test_001.test_gen_better_lots(False, 'Test 6', False, 6) ... ok
test_001.test_gen_better_lots(False, 'Test 7', False, 7) ... ok

Wow !!! Thats great.

Complete Code for testing

This is now my testing framework.

It is

  • More comprehensive
  • More sussinct
  • Easier to understand - Well I hope so
from MySimpleModule import MySimpleModule

cls=None

def test_000_init():
    global cls
    cls=MySimpleModule()
    assert cls


def test_030_isin_bad_data():
    global cls
    try:
        cls.isInData("a")
    except Exception as err:
        assert type(err) == ValueError

class report(object):
    def __call__(self, ExpectedResult,desc,value,n):
        self.description = 'Test {}:{}'.format(desc,value)
        assert ExpectedResult == value , '{} {}'.format(ExpectedResult, value)

def test_gen_better_lots():
    global cls
    for n in range(1, 4):
        yield report(), True,  "Test {}".format(n), check_this(n),n
    for n in range(4, 8):
        yield report(), False, "Test {}".format(n), check_this(n),n

def check_this(n):
    global cls
    return cls.isInData(n)

Coverage

When running nosetests with the option of --with-coverage a hidden file is created, called .coverage.

This is an XML file - however when reading it