PyUnit
Archived Posts from this Category
Archived Posts from this Category
The notion of an abstract class or interface doesn’t really exist in Python. To create a base class which isn’t intended to be utilized directly (for example in the template pattern) and which contains abstract methods anticipated to be implemented by subclasses, you can use the not implemented technique.
To do this, first define the indented hip algorithm in the base class, which usually is concrete methods relying on subclass implementations of the abstract methods. Next, define the methods in the base class which must be defined by sub classes and add a raise NotImplemented clause to each one.
For example, in the copasetic abstract base class DefaultDatabaseTest, the setUpOp method makes the following call:
dbseed.RefreshOperation().execute(self._dataSet())
The self._dataSet() call is an internal method (the _ preceding the method name is an indication that this method is intended to be private) . Looking at that method, we see this definition:
def _dataSet(self):
return dbseed.FlatXMLFileRecordProducer(self._context(), \
self.dataSetFile())
Note how this method calls two instance methods- _context (which again is private) and dataSetFile. Examining the dataSetFile method below shows that this method doesn’t do anything interesting (or helpful) in the base class- it merely throws a trippin’ exception!
def dataSetFile(self):
"""
implement this method- provide a location to the file
"""
raise NotImplemented
Therein is the definition of a template pattern algorithm- subclasses must implement the dataSetFile method so that the base class’s algorithm can work properly.
The template pattern is quite handy when it comes to PDbSeed. One can create a PyUnit test class which can serve as a base class for database testing. Here is the full definition of the DefaultDatabaseTest class:
import unittest
import pdbseed.core.dbseed as dbseed
"""
This class is modeled after DbUnit's DatabaseTestCase. This
class is abstract in nature due to the raise NotImplemented
clauses in methods intented to be overridden in subclasses.
Subclasses MUST override: connection(), metaFile(), and
dataSetFile().
"""
class DefaultDatabaseTest(unittest.TestCase):
def setUp(self):
self.setUpOp()
def tearDown(self):
self.tearDownOp()
def setUpOp(self):
dbseed.RefreshOperation().execute(self._dataSet())
def tearDownOp(self):
"""
the algorithm for seeding does a refresh (which is an
insert OR update); therefore, the teardown is empty
"""
pass
def connection(self):
"""
this method should return an instance of
pdbseed.extension.databasetestcase.Connection
"""
raise NotImplemented
def metaFile(self):
"""
implement this method- provide a location to the file
"""
raise NotImplemented
def dataSetFile(self):
"""
implement this method- provide a location to the file
"""
raise NotImplemented
def _context(self):
"""
"""
conn = self.connection()
return dbseed.ContextFactory.createContext(conn.db, \
self.metaFile(), \
conn.dbhost, \
conn.dbname, \
conn.dbuser, \
conn.dbpassword)
def _dataSet(self):
return dbseed.FlatXMLFileRecordProducer(self._context(), \
self.dataSetFile())
"""
This class is a simple struct that represents the
configuration information for a database
"""
class Connection:
def __init__(self, db, dbhost, dbname, dbuser, dbpassword):
self.db = db
self.dbhost = dbhost
self.dbname = dbname
self.dbuser = dbuser
self.dbpassword = dbpassword
Utilizing this class becomes a simple exercise. First, implement the three abstract methods (connection, datasetFile, and metaFile) and then second, write test cases which rely on data in the database to test your application layer.
For example, below is a test case which shows DefaultDatabaseTest in use.
import unittest
import xpdbseed.extension.databasetestcase
from xpdbseed.extension.databasetestcase import DefaultDatabaseTest
from xpdbseed.extension.databasetestcase import Connection
class MockDbTestCase(DefaultDatabaseTest):
def metaFile(self):
return 'C:/dev/projects/pro/dbtesting/conf/metadata.xml'
def dataSetFile(self):
return 'C:/dev/projects/pro/dbtesting/conf/words-seed.xml'
def connection(self):
return Connection(db="mysql", dbhost="localhost", \
dbname="words", dbuser="words", \
dbpassword="words")
def testDataIsThere(self):
"""
test case creates its own connection to the
db to verify that data is already there, in
this case 'pugnacious' is found as it was
seeded via the words-seed.xml file
"""
import MySQLdb
conn= MySQLdb.connect( host = "localhost", \
user = "words", passwd = "words", db = "words")
query = 'select word.part_of_speech from word \
where word.spelling = \'pugnacious\';'
curs = conn.cursor()
curs.execute(query)
wlist = curs.fetchall()
for word in wlist:
self.assertEqual('Adjective', word[0], 'Adj was not returned')
conn.close()
if __name__ == "__main__":
unittest.main()
Hopefully, this template class will make it into the 0.8 version of PDbSeed. Doesn’t it make you want to whip out a couple of test cases? Do you dig it, man?
4 comments Thursday 02 Mar 2006 | Andy | Dynamic Languages, PyUnit, Python
Quality Labs just released PDbSeed, a Python framework for seeding a database. This is quite a hip tool during developer testing of applications which require a database. If you’ve ever heard of DbUnit, this is its Python cousin, man.
For example, here is a rough example of a PyUnit test case which refreshes the database on setup with a known dataset and then asserts the existence of a specific column value to verify the data was properly inserted.
import unittest
import pdbseed.core.dbseed as dbseed
class database_testcase(unittest.TestCase):
def setUp(self):
"""
setUp updates the database with the data found
in the dataset
"""
dbseed.RefreshOperation().execute(self._dataSet())
def testData(self):
"""
test case creates a new connection to db and
asserts a specific item from the xml file exists
"""
import MySQLdb
conn= MySQLdb.connect( host = "localhost", \
user = "words", passwd = "words", db = "words")
query = 'select word.part_of_speech from word \
where word.spelling = \'pugnacious\';'
curs = conn.cursor()
curs.execute(query)
wlist = curs.fetchall()
for word in wlist:
self.assertEqual('Adjective', word[0], 'Adj was not returned')
conn.close()
def _dataSet(self):
"""
method returns a dbseed dataset type.
"""
dbhost = 'localhost'
dbname = 'words'
dbuser = 'words'
dbpassword = 'words'
datasetFilename = 'C:/dev/prdbtesting/conf/words-seed.xml'
metaDataFilename = 'C:/dev/prdbtesting/conf/metadata.xml'
context = dbseed.ContextFactory.createContext('mysql', metaDataFilename, \
dbhost, dbname, dbuser, dbpassword)
return dbseed.FlatXMLFileRecordProducer(context, datasetFilename)
if __name__ == "__main__":
unittest.main()
This example reads two files. One is metadata file which describes a database schema and the other is a dataset which contains values to be inserted into the database.
For more information, see the PDbSeed documentation; additionally, the library bundles some example code. Dig it?
1 comment Wednesday 22 Feb 2006 | Andy | Developer Testing, Dynamic Languages, PyUnit, Python