I am trying to write a simple test however my views are decorated with nested user_passes_test statements. They check things like a stripe subscription and is_authenticated. I have found various posts such as this which address how to bypass a decorator with patch but I can't quite work out how to integrate everything together.
tests.py
#patch('dashboard.views.authorised_base_user_checks', lambda func: func)
def test_dashboard_root_exists(self):
response = self.client.get('/dashboard/')
self.assertEqual(200, response.status_code)
decorator in views
def authorised_base_user_checks(view_func):
decorated_view_func = login_required(user_active(subscriber_exists(subscriber_valid(view_func))))
return decorated_view_func
views.py
#authorised_base_user_checks
def IndexView(request):
...
The above still fails to pass through the decorator.
Thanks!
This approach with patching of decorator most probably does not work because import of views module happens after the patching. If view has been already imported the decorator had been already applied to IndexView and patching the decorator function would have no effect at all.
You can reload the view module to overcome this:
import imp
import dashboard.views
#patch('dashboard.views.authorised_base_user_checks', lambda func: func)
def test_dashboard_root_exists(self):
# reload module to make sure view is decorated with patched decorator
imp.reload(views)
response = self.client.get('/dashboard/')
self.assertEqual(200, response.status_code)
# reload again
patch.stopall()
imp.reload(views)
Disclaimer: this code only demonstrates the idea. You need to make sure stopall and final reload always happens, so they should be in finally or in tearDown.
Related
I want to execute a custom logic block in a view before the PasswordResetView is called. This is what I am trying to do, which of course always fails regardless the code, I must be taking a non adequate route to achieving this. I need to save some information, do some internal notifications and only call the password reset view if a condition is met.
views.py
from django.contrib.auth.views import PasswordResetView
def user_password_reset(request):
# Do something here before loading the passwordresetview
# Logic here
return PasswordResetView.as_view(template_name = 'account/password_reset_form.html',
email_template_name='account/password_reset_email.html',
html_email_template_name='account/password_reset_email.html',
success_url = reverse_lazy('account:password_reset_done'))
Which is the standard way of achieving this?
Many thanks in advance.
You can call the function that is returned by the .as_view():
from django.contrib.auth.views import PasswordResetView
def user_password_reset(request):
# Do something here before loading the passwordresetview
# Logic here
return PasswordResetView.as_view(
template_name='account/password_reset_form.html',
email_template_name='account/password_reset_email.html',
html_email_template_name='account/password_reset_email.html',
success_url = reverse_lazy('account:password_reset_done')
)(request)
That being said, it might make more sense to just subclass the PasswordResetView, and for example overwrite the .dispatch(..) method.
I'm using pytest with Flask and want to test my views and templates but I'm unclear how best to do this.
I'm aware I can test the contents of the HTML output, e.g.:
def test_my_view(test_client):
# test_client is a fixture representing app.test_client()
response = test_client.get("/my-url")
assert b"<h1>My page title</h1>" in response.data
But there are things I'm not sure how best to do:
How do I test which template is being used by the view?
How do I test the context the view sends to the template? (e.g. check that login_form is an instance of LoginForm)
If I want to test that a more complex HTML tag is present, say a <form> tag with the correct action attribute, is the only way to check for the presence of the entire tag (e.g. <form method="get" class="form-lg" action="/other-url">) even if I'm not bothered about other attributes? How could I just check for the action, assuming other forms are on the page too?
I've realised that 1 and 2 can be solved by a solution like the one in this question, slightly altered for use with pytest.
Let's say we have this Flask view:
from flask import render_template
from app import app
#app.route("/my/view")
def my_view():
return render_template("my/template.html", greeting="Hello!")
We want to test that calling that URL uses the correct template, and that has the correct context data passed to it.
First, create a reusable fixture:
from flask import template_rendered
import pytest
#pytest.fixture
def captured_templates(app):
recorded = []
def record(sender, template, context, **extra):
recorded.append((template, context))
template_rendered.connect(record, app)
try:
yield recorded
finally:
template_rendered.disconnect(record, app)
I also have a test_client fixture for making requests in tests (something like the testapp fixture in Flask Cookiecutter or the test_client fixture in this tutorial).
Then write your test:
def test_my_view(test_client, captured_templates):
response = test_client.get("/my/view")
assert len(captured_templates) == 1
template, context = captured_templates[0]
assert template.name = "my/template.html"
assert "greeting" in context
assert context["greeting"] == "Hello!"
Note that you might have more than one element in captured_templates, depending on what your view does.
In my Flask applicaiton I have around 30 views which are decorated with the #login_required decorator.
My goal is: I want to call a python method - lets call it "refreshSession()" - every time a view which is #login_required decorated is called.
Is there a way to hook into this decorator? I don't want to write the line "refreshSession()" 30 times.
As far as i know, there's no way to hook directly into #login_required decorator.
Maybe you should try to call refreshSession on your Flask-Login user_loader function?
More information: https://flask-login.readthedocs.io/en/latest/#flask_login.LoginManager.user_loader
Something like this:
from flask_login import LoginManager
login_manager = LoginManager()
#login_manager.user_loader
def load_user(user_id):
refreshSession()
# Fetch user from database
user = User.get_by_id(user_id)
return user
Alternatively you could create your own login_required decorator, and then call refreshSession() inside it.
login_required decorator source: https://github.com/maxcountryman/flask-login/blob/3e521a326696cafbfbebfbb80a2fbffed68e6cf3/flask_login/utils.py#L231
I'm writing the tests for django views, Some of the views are making the external HTTP requests. While running the tests i dont want to execute these HTTP requests. Since during tests , data is being used is dummy and these HTTP requests will not behave as expected.
What could be the possible options for this ?
You could override settings in your tests and then check for that setting in your view. Here are the docs to override settings.
from django.conf import settings
if not settings.TEST_API:
# api call here
Then your test would look something like this
from django.test import TestCase, override_settings
class LoginTestCase(TestCase):
#override_settings(TEST_API=True)
def test_api_func(self):
# Do test here
Since it would be fairly messy to have those all over the place I would recommend creating a mixin that would look something like this.
class SensitiveAPIMixin(object):
def api_request(self, url, *args, **kwargs):
from django.conf import settings
if not settings.TEST_API:
request = api_call(url)
# Do api request in here
return request
Then, through the power of multiple inheritence, your views that you need to make a request to this api call you could do something similar to this.
class View(generic.ListView, SensitiveAPIMixin):
def get(self, request, *args, **kwargs):
data = self.api_request('http://example.com/api1')
This is where mocking comes in. In your tests, you can use libraries to patch the parts of the code you are testing to return the results you expect for the test, bypassing what that code actually does.
You can read a good blog post about mocking in Python here.
If you are on Python 3.3 or later, the mock library is included in Python. If not, you can download it from PyPI.
The exact details of how to mock the calls you're making will depend on what exactly your view code looks like.
Ben is right on, but here's some psuedo-ish code that might help. The patch here assumes you're using requests, but change the path as necessary to mock out what you need.
from unittest import mock
from django.test import TestCase
from django.core.urlresolvers import reverse
class MyTestCase(TestCase):
#mock.patch('requests.post') # this is all you need to stop the API call
def test_my_view_that_posts_to_an_api(self, mock_get):
response = self.client.get(reverse('my-view-name'))
self.assertEqual('my-value', response.data['my-key'])
# other assertions as necessary
I'm looking to do some tests and I'm not really familiar with the URLResolver quite yet but I'd like to solve this issue quickly.
In a TestCase, I'd like to add a URL to the resolver so that I can then use Client.get('/url/') and keep it separate from urls.py.
Since Django 1.8 using of django.test.TestCase.urls is deprecated. You can use django.test.utils.override_settings instead:
from django.test import TestCase
from django.test.utils import override_settings
urlpatterns = [
# custom urlconf
]
#override_settings(ROOT_URLCONF=__name__)
class MyTestCase(TestCase):
pass
override_settings can be applied either to a whole class or to a particular method.
https://docs.djangoproject.com/en/2.1/topics/testing/tools/#urlconf-configuration
In your test:
class TestMyViews(TestCase):
urls = 'myapp.test_urls'
This will use myapp/test_urls.py as the ROOT_URLCONF.
I know this was asked a while ago, but I thought I'd answer it again to offer something more complete and up-to-date.
You have two options to solve this, one is to provide your own urls file, as suggested by SystemParadox's answer:
class MyTestCase(TestCase):
urls = 'my_app.test_urls'
The other is to monkey patch your urls. This is NOT the recommended way to deal with overriding urls but you might get into a situation where you still need it. To do this for a single test case without affecting the rest you should do it in your setUp() method and then cleanup in your tearDown() method.
import my_app.urls
from django.conf.urls import patterns
class MyTestCase(TestCase):
urls = 'my_app.urls'
def setUp(self):
super(MyTestCase, self).setUp()
self.original_urls = my_app.urls.urlpatterns
my_app.urls.urlpatterns += patterns(
'',
(r'^my/test/url/pattern$', my_view),
)
def tearDown(self):
super(MyTestCase, self).tearDown()
my_app.urls.urlpatterns = self.original_urls
Please note that this will not work if you omit the urls class attribute. This is because the urls will otherwise be cached and your monkey patching will not take effect if you run your test together with other test cases.
Couldn't get it running with the answers above. Not even with the override_settings.
Found a solution which works for me. My usecase was to write some integration tests where I want to test put/post methods where I needed the urls from my app.
The main clue here is to use the set_urlconf function of django.urls instead of overwriting it in the class or using override_settings.
from django.test import TestCase
from django.urls import reverse, set_urlconf
class MyTests(TestCase):
#classmethod
def setUpClass(cls):
super().setUpClass()
set_urlconf('yourapp.urls') # yourapp is the folder where you define your root urlconf.
def test_url_resolving_with_app_urlconf(self):
response = self.client.put(
path=reverse('namespace:to:your:view-name'), data=test_data
)