Mastering Objectoriented Python
上QQ阅读APP看书,第一时间看更新

Unit testing and technology spikes

As part of object-oriented design, we'll often create technology spike modules that look like the code shown in this section. We'll break it down into three sections. First, we have the overall abstract test as follows:

import types
import unittest

class TestAccess( unittest.TestCase ):
    def test_should_add_and_get_attribute( self ):
        self.object.new_attribute= True
        self.assertTrue( self.object.new_attribute )
    def test_should_fail_on_missing( self ):
        self.assertRaises( AttributeError, lambda: self.object.undefined )

This abstract TestCase subclass defines a few tests that we're expecting a class to pass. The actual object being tested is omitted. It's referenced as self.object, but no definition is provided, making this TestCase subclass abstract. A setUp() method is required by each concrete subclass.

The following are three concrete TestAccess subclasses that will exercise three different kinds of objects:

class SomeClass:
    pass
class Test_EmptyClass( TestAccess ):
    def setUp( self ):
       self.object= SomeClass()
class Test_Namespace( TestAccess ):
    def setUp( self ):
       self.object= types.SimpleNamespace()
class Test_Object( TestAccess ):
    def setUp( self ):
       self.object= object()

The subclasses of the TestAccess classes each provide the required setUp() method. Each method builds a different kind of object for testing. One is an instance of an otherwise empty class. The second is an instance of types.SimpleNamespace. The third is an instance of object.

In order to run these tests, we'll need to build a suite that doesn't allow us to run the TestAccess abstract test.

The following is the rest of the spike:

def suite():
    s= unittest.TestSuite()
    s.addTests( unittest.defaultTestLoader.loadTestsFromTestCase(Test_EmptyClass) )
    s.addTests( unittest.defaultTestLoader.loadTestsFromTestCase(Test_Namespace) )
    s.addTests( unittest.defaultTestLoader.loadTestsFromTestCase(Test_Object) )
    return s

if __name__ == "__main__":
    t= unittest.TextTestRunner()
    t.run( suite() )

We now have concrete evidence that the object class can't be used the same way the types.SimpleNamespace class can be used. Further, we have a simple test class that we can use to demonstrate other designs that work (or don't work.) The tests, for example, demonstrate that types.SimpleNamespace behaves like an otherwise empty class.

We have omitted numerous details of potential unit test cases. We'll look at testing in depth in Chapter 15, Designing for Testability.