Create a Python module that selectively imports what test cases to run.
With these steps, we will explore selectively picking a smaller set of tests to facilitate a faster test run.
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 *
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
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))
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 + ";")
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 "---------------------------------"
recipe59.py
that only imports the SQL-based test case.from recipe59_test import EventCorrelatorIntegrationTests if __name__ == "__main__": import unittest unittest.main()
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 committedintegration.py
: Import a set of test cases that startup, interact, and then shutdown external systems like LDAP, databases, or other subsystemssecurity.py
: Import a set of test cases that are focused on various security scenarios, confirming both good and bad credential handlingall.py
: Import all test cases to make sure everything is workingThis 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.
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.