Django Test framework with file based Email backend server - python

I have formulated test cases in Django framework.
Use Case:
I am using API that register user by sending them an Email and when they click on the link provided in the Email their account get activated.
In my settings.py I am using
EMAIL_FILE_PATH ='django.core.mail.backends.filebased.EmailBackend'
which points to the local directory.
When running PyUnit test case from eclipse everything works file. Text file gets generated for each email sent
But, When i am using
python ./manage.py test <component_name>
the files does not generate.
Any insight what is the difference when I execute test case with ./manage.py and when I use pyUnit ?

It's possible to overwrite this aspect in Django if you want to use a specific email backend.
In django.test.utils, Django will change the e-mail backend to locmem as mentioned in the Django Testing documentation when Django sets up the testing environment:
def setup_test_environment():
...
mail.original_email_backend = settings.EMAIL_BACKEND
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
So if you want to enable sending e-mails for a test, you just need to change the setting to what you want.
from django.test.utils import override_settings
#override_settings(EMAIL_BACKEND='django.core.mail.backends.filebased.EmailBackend')
class MyTest(TestCase):
# your test case

The simple answer:
You can't do this without engineering your own email system, but that would probably be silly. I would suggest doing something else to verify that the code was successful without requiring the email to be sent. Like, run the code, assume the user clicks the link and create RequestFactory to get/post the link to run the view code associated with it.
From the Django Testing Application:
Email services
"If any of your Django views send email using Django's email functionality,
you probably don't want to send email each time you run a test using that
view. For this reason, Django's test runner automatically redirects all
Django-sent email to a dummy outbox. This lets you test every aspect of
sending email -- from the number of messages sent to the contents of each
message -- without actually sending the messages."

For somebody (like me) that need to use custom email backend for all tests, another solution would be to override TestRunner class and force settings change.
from django.conf import settings
from django.test.runner import DiscoverRunner
class CustomTestRunner(DiscoverRunner):
def setup_test_environment(self, **kwargs):
super().setup_test_environment(**kwargs)
settings.EMAIL_BACKEND = 'path.to.your.email.backend'
And after that register the test runner in settings:
TEST_RUNNER = 'path.to.CustomTestRunner'

Related

Django's user_logged_in signal not working when running a test case

I have this signal:
#receiver(user_logged_in)
def on_user_login(sender, request, **kwargs):
if request.user.is_superuser:
It all works when running the app. But when testing, this piece of code fails:
self.client.force_login(...)
The test fails with:
AttributeError: HttpRequest object has no attribute user
If I remove the signal, that test case runs just fine.
I have Django 1.10
In my test I was testing one of the admin views. I wanted to login the user first before doing this change. What I did not realize was that I really wanted to test just the admin view, not logging in.
So instead of using client for testing, I used RequestFactory and passed the user in it. No logging in necessary.
The original error was due to the fact that client in django tests does not add the user to the request.

Pyramid authentication testing with webtest

I configured my pyramid app in order to have an user object attached to request once it has been authenticated following the official tutorial. So far so good... but while it works perfectly and I can test it using a browser, I don't understand why in webtest tests user is not attached to the request.
I configured my test class in this way:
from my_pyramid_app import main as make_app
from webtest.app import TestApp
from pyramid import testing
class LoginTestCase(TestCase):
def setUp(self):
self.config = testing.setUp()
self.app = TestApp(make_app({}))
And in a test:
# submit valid login data to /login and expect redirect to "next"
response = self.app.post('/login', data, status=302)
redirect = response.follow()
It works as expected, user gets authenticated and redirected to the path specified in "next", but redirect.request does not contain user. Why? What should I do?
ps. the documentation of webtest says:
The best way to simulate authentication is if your application looks
in environ['REMOTE_USER'] to see if someone is authenticated. Then you
can simply set that value, like:
app.get('/secret', extra_environ=dict(REMOTE_USER='bob'))
but honestly it sounds demential to me :/ (I mean if I define a variable manually what is the sense of the test?!)
both webtest and pyramid use webob but this doesn't mean that pyramid's request is the same object than webtest's response.request
the only immutable object shared between webtest and the tested application is the environ dictionary.
This mean that you may be able to retrieve your user if you store it in request.environ with a key like 'myapp.user' (dot and lowercase are important, see PEP333).

Django Test : SESSION_KEY failing assertIn

I am using the Django Test client (django.test.Client) to run view tests. Upon attempting to use the Test Client on my index function that handles post requests for logins, it continually fails the test even though the authentication successfully occurs.
Heres my test:
def test_login(self):
response = self.client.post('/login/', {'username':'user', 'password':'pass'})
print response.content
self.assertIn(SESSION_KEY, self.client.session)
So the reason i know the login process successfully works is because response.content yields HTML data from another view that can only be access if request.user.is_authenticated() is true. In other words, they must be logged in for response.content to yield the "logged in page". So given this, i can tell that the function obviously works for its practical purpose of logging the user in, however, i've been scouring the docs for hours trying to figure out why i can't access SESSION_KEY from the client session. All my reading suggests that the django test client is in fact stateful in nature and should store the session.
Can someone shed some light on this?
Ok after much searching and asking around on #django, i made a working solution for Django 1.6.x
from django.contrib.auth import SESSION_KEY, get_user_model
from django.test import Client
def setUp(self):
self.client = Client()
def test_login_view(self):
user_pk = get_user_model()._default_manager.get(username__exact='test_username_here').pk
response = self.client.post('/login/', {'username':'test_username_here', 'password':'test_password_here'})
self.assertEqual(self.client.session[SESSION_KEY], user_pk)
The test_login_view function will be the one evaluating the view in my app that handles user logins from the template form. First, i grab user_pk which is the real primary key of the given user in the database. I used get_user_model() instead of User.objects.get() because the former allows you to reference regardless of whether the User model is modified or not. Of course you can use the latter as well. Second, i go ahead and send the post request using the test client just like a standard user's browser would. Finally, i discovered that self.client.session[SESSION_KEY] contains the primary key of the logged in user. (If the login was successful, otherwise, it will simply yield a KeyError)

Django Nose how to write this test?

I'm completely new to testing in Django. I have started by installing nose and selenium and now I want to test the following code (below) It sends an SMS message.
This is the actual code:
views.py
#login_required
def process_all(request):
"""
I process the sending for a single or bulk message(s) to a group or single contact.
:param request:
"""
#If we had a POST then get the request post values.
if request.method == 'POST':
batches = Batch.objects.for_user_pending(request.user)
for batch in batches:
ProcessRequests.delay(batch)
batch.complete_update()
return HttpResponseRedirect('/reports/messages/')
So where do I start? This is what I have done so far...
1) created a folder called tests and added init.py.
2) created a new python file called test_views.py (I'm assuming that's correct).
Now, how do I go about writing this test?
Could someone show me with an example of how I write the test for the view above?
Thank you :)
First of all, you don't need selenium for testing views. Selenium is a tool for high-level in-browser testing - it's good and useful when you are writing UI tests simulating a real user.
Nose is a tool that makes testing easier by providing features like automatic test discovery, supplies a number of helper functions etc. The best way to integrate nose with your django project is to use django_nose package. All you have to do is to:
add django_nose to INSTALLED_APPS
define TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
Then, every time you run python manage.py test <project_name> nose will be used to run your tests.
So, speaking about testing this particular view, you should test:
login_required decorator work - in other words, unauthenticated user will be redirected to the login page
if request.method is not POST, no messages sent + redirect to /reports/messages
sending SMS messages when POST method is used + redirect to /reports/messages
Testing first two statements is pretty straight-forward, but in order to test the last statement you need to provide more details on what is Batch, ProcessRequests and how does it work. I mean, you probably don't want to send real SMS messages during testing - this is where mocking will help. Basically, you need to mock (replace with your own implementation on the fly) Batch, ProcessRequests objects. Here's an example of what you should have in test_views.py:
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test.client import Client
from django.test import TestCase
class ProcessAllTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user('john', 'lennon#thebeatles.com', 'johnpassword')
def test_login_required(self):
response = self.client.get(reverse('process_all'))
self.assertRedirects(response, '/login')
def test_get_method(self):
self.client.login(username='john', password='johnpassword')
response = self.client.get(reverse('process_all'))
self.assertRedirects(response, '/reports/messages')
# assert no messages were sent
def test_post_method(self):
self.client.login(username='john', password='johnpassword')
# add pending messages, mock sms sending?
response = self.client.post(reverse('process_all'))
self.assertRedirects(response, '/reports/messages')
# assert that sms messages were sent
Also see:
https://docs.djangoproject.com/en/dev/topics/testing/
Django Testing Guide
Getting Started with Python Mock
Hope that helps.

How to confirm user created by create_user in flask security mongoengine app?

I have a python flask app using mongoengine and flask-security built up from the examples to expose all of the confirmation, registration, tracking, and recovery functionality.
Everything works except that a user created imperatively in the code using:
MongoEngineUserDatastore.create_user(...)
cannot login. That is, when you try to login with this user, you get an error message:
"Email requires confirmation"
Since the email with a hashed URL has not been sent, there is no way to confirm. Is there a parameter I can pass somewhere to confirm this user on creation or set the confirmed flag somewhere?
Here's my code:
I figured it out by confirming a newly registered user and examining mongodb to see what fields were added. Turns out that the required field for confirmation is confirmed_at, which must have a datetime so:
import datetime
# Create a user to test with
#app.before_first_request
def create_user():
user_datastore.create_user(
email='me#mydomain.com',
password=utils.encrypt_password('password'),
confirmed_at=datetime.datetime.now())
I've updated the gist here:
https://gist.github.com/davidthewatson/327776905ef30815c138
When you create your test user you need to make them active eg:
#app.before_first_request
def create_user():
user_datastore.create_user(
email='me#mydomain.com',
password=utils.encrypt_password('password'),
active=True)

Categories