DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Languages
  4. Developing (a.k.a. Testing) in Python - Part 3: Monkey Patching

Developing (a.k.a. Testing) in Python - Part 3: Monkey Patching

Daniel Gottlieb user avatar by
Daniel Gottlieb
·
Nov. 16, 11 · Interview
Like (0)
Save
Tweet
Share
4.42K Views

Join the DZone community and get the full member experience.

Join For Free

* This is part 3 of a series on Python test cases. *

 At Fiesta we run our own custom mailserver. Since we handle a lot of incoming mail, we have to be confident our updates don’t break the mailserver. As can be expected, we have a large amount of test code and run it very often.

dgottlieb@mango-chutney:~$ wc -l ./fiesta/*.py
 ...
 11850 total
dgottlieb@mango-chutney:~$ wc -l ./fiesta/test/*.py
 ...
 13566 total


We want to test end-to-end delivery of email, but we don’t want our tests leading to a lot of unintentionally sent messages. This is where monkey patching comes in. Because Python has open classes and modules, we can easily redefine methods on the fly. To test our mailserver, we replace some portions of the Python smtplib module during test runs. Let’s take a look at how it’s done:

mailbox = {}
class SMTP(object):
    def sendmail(self, from, recipients, data):
        message = email_parser.from_string(data)
        for recipient in recipients:
	    if recipient not in mailbox:
                mailbox[recipient] = [message]
            else:
                mailbox[recipient].append(message)
smtplib.SMTP = SMTP


Here we are actually replacing the entire SMTP object in smtplib with our own custom implementation. Our class defines a sendmail() function that takes the passed in email and adds it to a mailbox dictionary. The dictionary is just a map from email addresses to lists of messages sent to those addresses. In our tests when we expect an email to be sent to someone we simply peek into the mailbox dictionary:

self.assertIn('dan@corp.fiesta.cc', self.mailbox)
self.assertEqual(1, len(self.mailbox['dan@corp.fiesta.cc']))
self.assertEqual('Party On', self.mailbox['dan@corp.fiesta.cc'][0].subject)


Another example of monkey patching in the Fiesta test code is when we test links that will automatically log a user in without requiring a password. These links are generated by concatenating the User ID, the destination page and a time when the link should expire (and then signing the URL to prevent forgery). What we want to test is the timeout portion of these links, but it would be ideal if we did not have to have the test program sleep for any amount of time. This is how we monkey patch the time() method to go forward in time:

auto_login_link = rewrite.add_login('dan@corp.fiesta.cc', '/settings')
user_obj, link, validation_error = rewrite.validate_link(auto_login_link)
self.assertEquals(None, validation_error)

old_time = time.time
time.time = lambda: old_time() + 30 * 24 * 60 * 60 #return a time 1 month in the future
user_obj, link, validation_error = rewrite.validate_link(auto_login_link)
self.assertEquals("Expired Link", validation_error)

time.time = old_time


For those that haven’t had a chance to play with monkey patching, I hope this helps you come up with some clever, but clear (!) ways to test your code. It’s especially useful for testing methods that make library calls that need to exhibit less common/hard to force behavior.

Party on,

Dan

 

source: http://blog.fiesta.cc/post/12336430127/testing-in-python-part-3-monkey-patching

Python (language) Testing

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • The Importance of Delegation in Management Teams
  • The Data Leakage Nightmare in AI
  • The Future of Cloud Engineering Evolves
  • Secrets Management

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: