[Tutor] Writing unit tests that involve email
Arnel Legaspi
jalespring at gmail.com
Sun Jul 14 02:32:53 CEST 2013
Hello,
I have written a small Python script that allows the user to post to
their blogs via email, converting the draft written in Markdown &
sending it to a blog's email address (like what they had in Posterous
back then).
https://bitbucket.org/acl79/downpost
At this point, I've been trying to write unit tests for it to get some
practice writing unit tests for other possibly larger projects in the
future. I've been using the dsmtpd script available at:
https://github.com/matrixise/dsmtpd
to run a SMTP server in the background and route all the emails being
sent by my script to localhost. The script allows saving the messages to
an optional Maildir directory, and I wanted to use that to be able to
check various fields in the email for correctness in the unit tests.
What I'm having difficulty is getting the unit tests to properly run the
script being tested and send the email, get the proper Maildir message,
and be able to read it via the unit test script. Right now, even when
the SMTP server is not running, all the tests for checking the sent
email pass, which is not what I intended.
I know the SMTP server works, because if I send some email using another
Python or Powershell script, the script logs on the screen that it did
receive the email.
How should I approach this?
Here is the unit test script I have (some lines may get wrapped):
#!/usr/bin/env python
import os
import sys
import argparse
import ConfigParser
import unittest
import mailbox
MKDOWN_TEXT = """
**Lorem ipsum dolor sit amet**, consectetur adipiscing elit. Sed
dapibus dui nibh, ut fermentum dolor tristique fringilla. Pellentesque
tristique sagittis dapibus. Nunc pellentesque nisi vitae arcu lacinia,
nec ultricies libero rutrum. *Vivamus* enim augue, malesuada in dui ac,
laoreet faucibus dolor.
* Aliquam eu nisi dictum, tristique eros ut, eleifend elit.
* Pellentesque lacinia sodales erat non rhoncus.
* Aliquam mattis ullamcorper purus sit amet sodales.
Ut mollis ligula id sapien vehicula fringilla eu auctor diam.
Praesent consequat tristique arcu, nec iaculis metus porta at.
Nam mauris orci, congue vel consectetur vitae, viverra adipiscing
urna.
> Donec tristique leo erat, sit amet feugiat velit semper sit amet.
> Nunc sagittis libero risus, at mollis mauris pharetra quis. Vivamus
> adipiscing porttitor velit, sit amet sodales risus mollis quis.
Donec sed risus orci. Cras fringilla at lorem ut venenatis.
Pellentesque varius lorem neque, euplace rat eros dictum nec.
"""
HTML_TXT = """
<p><strong>Lorem ipsum dolor sit amet</strong>, consectetur adipiscing
elit. Sed
dapibus dui nibh, ut fermentum dolor tristique fringilla. Pellentesque
tristique sagittis dapibus. Nunc pellentesque nisi vitae arcu lacinia,
nec ultricies libero rutrum. <em>Vivamus</em> enim augue, malesuada in
dui ac,
laoreet faucibus dolor.</p>
<ul>
<li>Aliquam eu nisi dictum, tristique eros ut, eleifend elit.</li>
<li>Pellentesque lacinia sodales erat non rhoncus.</li>
<li>Aliquam mattis ullamcorper purus sit amet sodales.</li>
</ul>
<p>Ut mollis ligula id sapien vehicula fringilla eu auctor diam.
Praesent consequat tristique arcu, nec iaculis metus porta at.
Nam mauris orci, congue vel consectetur vitae, viverra adipiscing
urna.</p>
<blockquote>
<p>Donec tristique leo erat, sit amet feugiat velit semper sit amet.
Nunc sagittis libero risus, at mollis mauris pharetra quis. Vivamus
adipiscing porttitor velit, sit amet sodales risus mollis quis.</p>
</blockquote>
<p>Donec sed risus orci. Cras fringilla at lorem ut venenatis.
Pellentesque varius lorem neque, euplace rat eros dictum nec.</p>
"""
TESTARGS = []
def clear_args():
"""
Clear the arguments & the internal list (TESTARGS) used
for test arguments. This is needed to allow the script to
be tested with different arguments.
"""
del sys.argv[1:]
TESTARGS[:] = []
return
def add_testfiles(contentfile='post.md', configfile='blog.conf'):
"""
Clear the arguments & the internal list used. Add the
regular test files into the argument list instead.
"""
clear_args()
TESTARGS.append(contentfile)
TESTARGS.append(configfile)
TESTARGS.append('--debug')
sys.argv.append(TESTARGS)
return
def create_configfile():
"""
Create a dummy config file for testing purposes.
"""
parser = ConfigParser.SafeConfigParser(allow_no_value=True)
parser.add_section('blog')
parser.set('blog', 'email', 'test at email.net')
parser.add_section('smtp')
parser.set('smtp', 'server', '127.0.0.1')
parser.set('smtp', 'port', '8000')
parser.set('smtp', 'username', 'user at wherever.com')
parser.set('smtp', 'password', 'DeJpkZa3WL')
with open('blog.conf', 'wb') as configfile:
parser.write(configfile)
class TestReadingSendingPostFile(unittest.TestCase):
def setUp(self):
""" Prepare a post file for testing. """
create_configfile()
with open('post.md', 'wb') as postfile:
postfile.write(MKDOWN_TEXT)
self.mbox = mailbox.Maildir('mail_dir')
def test_wrongconfigfile(self):
clear_args()
add_testfiles('post.md', 'bloginfo.ini')
self.assertRaises(IOError)
def test_wrongpostfile(self):
clear_args()
add_testfiles('some_nonexistent_file.md', 'blog.conf')
self.assertRaises(IOError)
def test_sendpost(self):
clear_args()
add_testfiles()
for message in self.mbox:
self.assertEqual(message['subject'], 'post')
self.assertEqual(message.get_payload(), HTML_TXT)
def test_sendrawpost(self):
clear_args()
TESTARGS.append('--raw')
add_testfiles()
for message in self.mbox:
self.assertEqual(message['subject'], 'post')
self.assertEqual(message.get_payload(), MKDOWN_TEXT)
def test_sendpost_withunderscores(self):
clear_args()
os.rename('post.md',
'This_Should_Be_Posted_Without_Underscores.md')
add_testfiles('This_Should_Be_Posted_Without_Underscores.md',
'blog.conf')
for message in self.mbox:
self.assertEqual(message['subject'],
'This Should Be Posted Without Underscores')
self.assertEqual(message.get_payload(), HTML_TXT)
def test_sendpost_withspaces(self):
clear_args()
os.rename('post.md', 'This Should Be Posted As Is.md')
add_testfiles('This Should Be Posted As Is.md', 'blog.conf')
for message in self.mbox:
self.assertEqual(message['subject'],
'This Should Be Posted As Is')
self.assertEqual(message.get_payload(), HTML_TXT)
def test_sendpost_withsettitle(self):
clear_args()
TESTARGS.append('-t "This will be the new title"')
add_testfiles()
for message in self.mbox:
self.assertEqual(message['subject'],
'This will be the new title')
self.assertEqual(message.get_payload(), HTML_TXT)
def tearDown(self):
if os.path.exists('post.md'):
os.remove('post.md')
if os.path.exists('This_Should_Be_Posted_Without_Underscores.md'):
os.remove('This_Should_Be_Posted_Without_Underscores.md')
if os.path.exists('This Should Be Posted As Is.md'):
os.remove('This Should Be Posted As Is.md')
os.remove('blog.conf')
if __name__ == '__main__':
argp = argparse.ArgumentParser()
argp.add_argument('unittest_args', nargs='*')
args = argp.parse_args()
unit_args = [sys.argv[0]] + args.unittest_args
unittest.main(argv=unit_args)
Any comments on this & the script I'm writing tests for would be
appreciated.
Thanks,
Arnel
More information about the Tutor
mailing list