Unit testing and mocking email sender in Python with Google AppEngine
Question: I'm a newbie to python and the app engine.

I have this code that sends an email based on request params after some auth
logic. in my Unit tests (i'm using
[GAEUnit](http://code.google.com/p/gaeunit/)), how do I confirm an email with
specific contents were sent? - i.e. how do I mock the emailer with a fake
emailer to verify send was called?

    
    
    class EmailHandler(webapp.RequestHandler):
     def bad_input(self):
      self.response.set_status(400)
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write("<html><body>bad input </body></html>")
    
     def get(self):
      to_addr = self.request.get("to")
      subj = self.request.get("subject")
      msg = self.request.get("body")
      if not mail.is_email_valid(to_addr):
        # Return an error message...
        #   self.bad_input()
        pass
    
      # authenticate here
    
      message = mail.EmailMessage()
      message.sender = "my.company@gmail.com"
      message.to = to_addr
      message.subject = subj
      message.body = msg
      message.send()
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write("<html><body>success!</body></html>")
    

And the unit tests,

    
    
    import unittest
    from webtest import TestApp
    from google.appengine.ext import webapp
    from email import EmailHandler
    
    class SendingEmails(unittest.TestCase):
    
      def setUp(self):
        self.application = webapp.WSGIApplication([('/', EmailHandler)], debug=True)
    
      def test_success(self):
        app = TestApp(self.application)
        response = app.get('http://localhost:8080/send?to=vijay.santhanam@gmail.com&body=blah_blah_blah&subject=mySubject')
        self.assertEqual('200 OK', response.status)
        self.assertTrue('success' in response)
        # somehow, assert email was sent
    


Answer: You could also override the `_GenerateLog` method in the `mail_stub` inside
AppEngine.

Here is a parent TestCase class that I use as a mixin when testing that
e-mails are sent:

    
    
    from google.appengine.api import apiproxy_stub_map, mail_stub
    
    __all__ = ['MailTestCase']
    
    class MailTestCase(object):
        def setUp(self):
            super(MailTestCase, self).setUp()
            self.set_mail_stub()
            self.clear_sent_messages()
    
        def set_mail_stub(self):
            test_case = self
            class MailStub(mail_stub.MailServiceStub):
                def _GenerateLog(self, method, message, log, *args, **kwargs):
                    test_case._sent_messages.append(message)
                    return super(MailStub, self)._GenerateLog(method, message, log, *args, **kwargs)
    
            if 'mail' in apiproxy_stub_map.apiproxy._APIProxyStubMap__stub_map:
                del apiproxy_stub_map.apiproxy._APIProxyStubMap__stub_map['mail']
    
            apiproxy_stub_map.apiproxy.RegisterStub('mail', MailStub())
    
        def clear_sent_messages(self):
            self._sent_messages = []
    
        def get_sent_messages(self):
            return self._sent_messages
    
        def assertEmailSent(self, to=None, sender=None, subject=None, body=None):
            for message in self.get_sent_messages():
                if to and to not in message.to_list(): continue
                if sender and sender != message.sender(): continue
                if subject and subject != message.subject(): continue
                if body and body not in message.textbody(): continue
                return
    
            failure_message = "Expected e-mail message sent."
    
            args = []
            if to: args.append('To: %s' % to)
            if sender: args.append('From: %s' % sender)
            if subject: args.append('Subject: %s' % subject)
            if body: args.append('Body (contains): %s' % body)
    
            if args:
                failure_message += ' Arguments expected: ' + ', '.join(args)
    
            self.fail(failure_message)
    

After that, a sample test case might look like:

    
    
    import unittest, MailTestCase
    
    class MyTestCase(unittest.TestCase, MailTestCase):
        def test_email_sent(self):
            send_email_to('test@example.org') # Some method that would send an e-mail.
            self.assertEmailSent(to='test@example.org')
            self.assertEqual(len(self.get_sent_messages()), 1)
    

