Defining a subset of test cases using import statements

Create a Python module that selectively imports what test cases to run.

How to do it...

With these steps, we will explore selectively picking a smaller set of tests to facilitate a faster test run.

  1. Create a test module called recipe59_test.py for writing some tests against our network application.
    import logging
    from network import *
    import unittest
    from springpython.database.factory import *
    from springpython.database.core import *
  2. Create a test case that removes the database connection and stubs out the data access functions.
    class EventCorrelatorUnitTests(unittest.TestCase):
        def setUp(self):
            db_name = "recipe59.db"
            factory = Sqlite3ConnectionFactory(db=db_name)
            self.correlator = EventCorrelator(factory)
    
            # We "unplug" the DatabaseTemplate so that
            # we don't talk to a real database.
            self.correlator.dt = None
    
            # Instead, we create a dictionary of
            # canned data to return back
            self.return_values = {}
    
            # For each sub-function of the network app,
            # we replace them with stubs which return our
            # canned data.
    
            def stub_store_event(event):
                event.id = self.return_values["id"]
                return event, self.return_values["active"]
            self.correlator.store_event = stub_store_event
    
            def stub_impact(event):
                return (self.return_values["services"],
                        self.return_values["equipment"])
            self.correlator.impact = stub_impact
    
            def stub_update_service(service, event):
                return service + " updated"
            self.correlator.update_service = stub_update_service
    
            def stub_update_equip(equip, event):
                return equip + " updated"
            self.correlator.update_equipment = stub_update_equip
  3. Create a test method that creates a set of canned data values, invokes the application's process method, and then verifies the values.
        def test_process_events(self):
            # For this test case, we can preload the canned data,
            # and verify that our process function is working
            # as expected without touching the database.
    
            self.return_values["id"] = 4668
            self.return_values["active"] = True
            self.return_values["services"] = ["service1",
                                              "service2"]
            self.return_values["equipment"] = ["device1"]
    
            evt1 = Event("pyhost1", "serverRestart", 5)
    
            stored_event, is_active, 
               updated_services, updated_equipment = 
                         self.correlator.process(evt1)
    
            self.assertEquals(4668, stored_event.id)
            self.assertTrue(is_active)
    
            self.assertEquals(2, len(updated_services))
            self.assertEquals(1, len(updated_equipment))
  4. Create another test case that preloads the database using a SQL script (see Chapter 7, Building a network management application for details about the SQL script).
    class EventCorrelatorIntegrationTests(unittest.TestCase):
        def setUp(self):
            db_name = "recipe59.db"
            factory = Sqlite3ConnectionFactory(db=db_name)
            self.correlator = EventCorrelator(factory)
            dt = DatabaseTemplate(factory)
            sql = open("network.sql").read().split(";")
            for statement in sql:
                dt.execute(statement + ";")
  5. Write a test method that calls the network application's process method, and then prints out the results.
        def test_process_events(self):
            evt1 = Event("pyhost1", "serverRestart", 5)
    
            stored_event, is_active, 
               updated_services, updated_equipment = 
                         self.correlator.process(evt1)
    
            print "Stored event: %s" % stored_event
            if is_active:
                print "This event was an active event."
    
            print "Updated services: %s" % updated_services
            print "Updated equipment: %s" % updated_equipment
            print "---------------------------------"
  6. Create a new file called recipe59.py that only imports the SQL-based test case.
    from recipe59_test import EventCorrelatorIntegrationTests
    
    if __name__ == "__main__":
        import unittest
        unittest.main()
  7. Run the test module.
    How to do it...

How it works...

We need to write various test cases to cover the different levels of testing we need. By separating the test runner from the test case, we were able to decide to only run the test that integrated with the database.

Why would we do this? In our situation, we only have one unit test and it runs pretty quickly. Do you think that a real-world application with months or years of development and a corresponding test suite will run as quickly? Of course not!

Some of the tests may be complex. They may involve talking to real systems, parsing huge sample data files, and other time consuming tasks. This could realistically take minutes or hours to run.

When we are about to make a presentation to a customer, we don't need a long running test suite. Instead, we need to be able to run a quick subset of these tests that gives us the confidence that things are working. Using Python's import statements makes this easy to define.

Some suites we may want to think about include:

  • pulse.py: Import a set of test cases that provide broad, yet shallow testing of the application, to verify the system "has a pulse"
  • checkin.py: Import a set of test cases that are currently functioning and provide enough confidence that code is ready to be committed
  • integration.py: Import a set of test cases that startup, interact, and then shutdown external systems like LDAP, databases, or other subsystems
  • security.py: Import a set of test cases that are focused on various security scenarios, confirming both good and bad credential handling
  • all.py: Import all test cases to make sure everything is working

This is just a sample of the types of test modules we could define. It's possible to define a module for each subsystem we handle. But since we are talking about smoke testing, we may want to think more broadly, and instead pick some key tests from each subsystem and tie them together to give us a sense that the application is working.

Security, checking, and integration aren't smoke tests!

That is absolutely right. The previous list shows that using Python import statements isn't confined to defining smoke test suites. It can be used to bundle together test cases that serve differing needs. So why bring this up since we are talking about smoke tests? I wanted to convey how useful this mechanism is to organizing tests and that it extends beyond smoke testing.

What provides good flexibility?

In order to have good flexibility in being able to pick test classes, we should avoid making the test classes too big. But putting each test method inside a different class is probably too much.

See also

  • Chapter 7, Building a network management application
  • Leaving out integration tests
..................Content has been hidden....................

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