testing in those hard to reach places
DESCRIPTION
KiwiPyCon2011, Wellington, Sunday, Track 1, Testing in those hard to reach places by Lee BeggTRANSCRIPT
![Page 1: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/1.jpg)
Testing in Hard to Reach Places
Lee BeggBegg Digital
![Page 2: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/2.jpg)
Overview
● Goals● The module● Refactoring● SqlAlchemy tricks● Mocking hard things
Code: http://github.com/llnz/kiwipycon2011testingSlides: http://www.beggdigital.com/media/kiwipycon2011testing.pdf
![Page 3: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/3.jpg)
Goals
● Make sure it works properly● All the time● In all conditions
● Unit testing to check● Cover 100%● Might still miss cases
![Page 4: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/4.jpg)
Example moduledef main(): '''Check the tripwire, email if the latest values have an RMS value over 2''' session = model.Session() values = session.query(model.MeasuredValue).order_by(model.MeasuredValue.desc()).limit(20).all() totalsq = 0 for value in values: totalsq += value.value**2 rms = math.sqrt(totalsq) if rms > 2: body = """Database trip wire has been tripped
RMS value was: %s"""
subject = "Tripwire report %s" % datetime.date.today() msg = MIMEText(body) msg['Subject'] = '[DBTW] %s' % subject msg['To'] = ','.join(settings.TO_ADDRESSES) msg['From'] = settings.FROM_ADDRESS server = smtplib.SMTP('localhost') result = server.sendmail(settings.FROM_ADDRESS, settings.TO_ADDRESSES, msg.as_string()) if len(result) != 0: print "sendmail failures: %s" % result server.quit()
![Page 5: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/5.jpg)
Refactoring● Refactor out components to test
def calcRMS(values): totalsq = 0.0 for value in values: totalsq += value**2 rms = math.sqrt(totalsq/len(values)) return rms
class RMSTest(unittest.TestCase): '''Test the RMS calculations''' def testRMSCalc(self): testvalues = [ ([0], 0), ([1], 1), ([2], 2), ([0, 0], 0), ([1, 1], 1), ([0] * 20, 0), ([1] * 20, 1), ([0, 0, 0, 1], 0.5), ([3, 1, 3, 0, 1], 2), ] for values, expected in testvalues: result = tripwire.calcRMS(values) self.assertAlmostEqual(result, expected, msg='rmsCalc(%s) gave %s, expected %s' % (values, result, expected))
![Page 6: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/6.jpg)
SqlAlchemy Tricks
● Change the database in the unittest setUp method
● Done internally by Django
#override database setting
from dbtripwire import settings
settings.DATABASE_URL = 'sqlite:///:memory:'
from dbtripwire.model import initDatabase, dropDatabase
![Page 7: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/7.jpg)
SqlAlchemy Tricks
class DatabaseTestSetup(object): '''Create the database in the setUp, and drop it in tearDown Used to abstract this away from all the unittests that use the Database. Must be the first class inherited from, or TestCase will override these methods, not the other way around. ''' def setUp(self): '''Initialise the database with the tables''' initDatabase()
def tearDown(self): '''Drop the tables''' dropDatabase()
![Page 8: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/8.jpg)
SqlAlchemy Tricksclass ModelTest(DatabaseTestSetup, unittest.TestCase): '''Test the model classes''' def testMeasuredValueTable(self): '''MeasuredValue table test''' session = model.Session() self.assertEqual(session.query(model.MeasuredValue).count(), 0) mv = model.MeasuredValue(5) self.assert_(mv) session.add(mv) session.commit() self.assertEqual(session.query(model.MeasuredValue).count(), 1) mv1 = session.query(model.MeasuredValue).one() self.assertEqual(mv1.id, 1) self.assertEqual(mv1.value, 5) #don't forget to test the __repr__ string self.assertEqual(repr(mv1), "<MeasuredValue(1, 5)>") session.delete(mv1) session.commit() self.assertEqual(session.query(model.MeasuredValue).count(), 0)
![Page 9: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/9.jpg)
Mock
● Creating fake “mock” objects/classes/modules● Replace things you couldn't normally control● A few frameworks available● Very useful in replacing network connections
● Httplib for example● Smtplib for another
![Page 10: Testing in those hard to reach places](https://reader036.vdocuments.us/reader036/viewer/2022081821/5552bfcdb4c905920f8b4776/html5/thumbnails/10.jpg)
Mock exampleclass SendEmailTest(unittest.TestCase): '''Test sending email''' def testSendEmail(self): tt = minimock.TraceTracker() smtpconn = minimock.Mock('smtplib.SMTP', tracker=tt) minimock.mock('smtplib.SMTP', mock_obj=smtpconn) smtpconn.mock_returns = smtpconn smtpconn.sendmail.mock_returns = {} tripwire.sendEmail(2.5, datetime.date(2011, 8, 16)) expected = r"""Called smtplib.SMTP('localhost')Called smtplib.SMTP.sendmail( '[email protected]', ['[email protected]', '[email protected]'], 'Content-Type: text/plain; charset="us-ascii"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nSubject: [DBTW] Tripwire Report 2011-08-16\nTo: [email protected], [email protected]\nFrom: [email protected]\n\nDatabase trip wire has been tripped.\n\nRMS value was: 2.5\n')Called smtplib.SMTP.quit()""" self.assertTrue(tt.check(expected), tt.diff(expected)) minimock.restore()