Skip to content

Commit

Permalink
Add Django email testing support
Browse files Browse the repository at this point in the history
This commit adds support for inspecting emails sent that were sent by the Django server started by lettuce.
  • Loading branch information
Lorin Hochstein committed Jan 12, 2013
1 parent 2c982d9 commit 234636c
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 2 deletions.
26 changes: 26 additions & 0 deletions docs/recipes/django-lxml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,32 @@ Notice the ``terrain.py`` file at the project root, there you can
populate the :ref:`lettuce-world` and organize your features and steps
with it :)

Checking email
==============

When you run your Django server under lettuce, emails sent by your server
do not get transmitted over the Internet. Instead, these emails are
added to a :class:`multiprocessing.Queue` object at
``lettuce.django.mail.queue``.

Example:

.. highlight:: python

::

from lettuce import step
from lettuce.django import mail
from nose.tools import assert_equals


@step(u'an email is sent to "([^"]*?)" with subject "([^"]*)"')
def email_sent(step, to, subject):
message = mail.queue.get(True, timeout=5)
assert_equals(message.subject, subject)
assert_equals(message.recipients(), [to])


Running without HTTP server
===========================

Expand Down
Empty file added lettuce/django/mail/__init__.py
Empty file.
13 changes: 13 additions & 0 deletions lettuce/django/mail/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
Email backend that sends mails to a multiprocessing queue
"""
from lettuce.django import mail
from django.core.mail.backends.base import BaseEmailBackend


class QueueEmailBackend(BaseEmailBackend):

def send_messages(self, messages):
for message in messages:
mail.queue.put(message)
return len(messages)
19 changes: 17 additions & 2 deletions lettuce/django/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,15 @@
except ImportError:
pass

from lettuce.django import mail
from lettuce.registry import call_hook


def create_mail_queue():
mail.queue = multiprocessing.Queue()
return mail.queue


class LettuceServerException(WSGIServerException):
pass

Expand Down Expand Up @@ -104,10 +110,16 @@ class ThreadedServer(multiprocessing.Process):
lock = multiprocessing.Lock()
daemon = True

def __init__(self, address, port, *args, **kw):
def __init__(self, address, port, mail_queue, *args, **kw):
multiprocessing.Process.__init__(self)
self.address = address
self.port = port
self.mail_queue = mail_queue

def configure_mail_queue(self):
mail.queue = self.mail_queue
settings.EMAIL_BACKEND = \
'lettuce.django.mail.backends.QueueEmailBackend'

@staticmethod
def get_real_address(address):
Expand Down Expand Up @@ -162,6 +174,8 @@ def run(self):

open(pidfile, 'w').write(unicode(os.getpid()))

self.configure_mail_queue()

connector = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
Expand Down Expand Up @@ -217,7 +231,8 @@ class Server(object):
def __init__(self, address='0.0.0.0', port=None):
self.port = int(port or getattr(settings, 'LETTUCE_SERVER_PORT', 8000))
self.address = unicode(address)
self._actual_server = ThreadedServer(self.address, self.port)
queue = create_mail_queue()
self._actual_server = ThreadedServer(self.address, self.port, queue)

def start(self):
"""Starts the webserver thread, and waits it to be available"""
Expand Down
2 changes: 2 additions & 0 deletions lettuce/django/tests/functional/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test:
PYTHONPATH=`pwd` DJANGO_SETTINGS_MODULE=djangoapp django-admin.py harvest email.feature
25 changes: 25 additions & 0 deletions lettuce/django/tests/functional/djangoapp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
A minimal Django app, just one file.
See: http://olifante.blogs.com/covil/2010/04/minimal-django.html
"""
import os
from django.conf.urls.defaults import patterns
from django.core.mail import send_mail

from django.http import HttpResponse
filepath, extension = os.path.splitext(__file__)
ROOT_URLCONF = os.path.basename(filepath)
INSTALLED_APPS = (
"lettuce.django"
)


def mail(request):
send_mail('Subject here', 'Here is the message.', 'from@example.com',
['to@example.com'], fail_silently=False)
return HttpResponse('Mail has been sent')

urlpatterns = patterns('', (r'^mail/$', mail))
7 changes: 7 additions & 0 deletions lettuce/django/tests/functional/email.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: Check email sent by Django server

Scenario: Access a web page which triggers an email
Given I visit "/mail/"
Then I see "Mail has been sent"
and an email is sent to "to@example.com" with subject "Subject here"

22 changes: 22 additions & 0 deletions lettuce/django/tests/functional/steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from lettuce import world, step
from lettuce.django import mail
from lettuce.django import django_url

from nose.tools import assert_equals


@step(u'I visit "([^"]*)"')
def visit(step, url):
world.browser.visit(django_url(url))


@step(u'I see "([^"]*)"')
def see(step, text):
assert world.browser.is_text_present(text)


@step(u'an email is sent to "([^"]*?)" with subject "([^"]*)"')
def email_sent(step, to, subject):
message = mail.queue.get(True, timeout=5)
assert_equals(message.subject, subject)
assert_equals(message.recipients(), [to])
12 changes: 12 additions & 0 deletions lettuce/django/tests/functional/terrain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from splinter.browser import Browser
from lettuce import before, after, world


@before.harvest
def setup(server):
world.browser = Browser()


@after.harvest
def cleanup(server):
world.browser.quit()
25 changes: 25 additions & 0 deletions lettuce/django/tests/functional/test_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os
import sys
import commands

from tests.asserts import assert_not_equals
from lettuce.fs import FileSystem

current_directory = FileSystem.dirname(__file__)

OLD_PYTHONPATH = os.getenv('PYTHONPATH', ':'.join(sys.path))


def teardown():
os.environ['PYTHONPATH'] = OLD_PYTHONPATH


def test_email():
'lettuce should be able to receive emails sent from django server'
os.environ['PYTHONPATH'] = current_directory
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoapp'

status, out = commands.getstatusoutput(
"django-admin.py harvest email.feature --verbosity=2")

assert_not_equals(status, 0)

0 comments on commit 234636c

Please sign in to comment.