Instant integration of Ian Bicking's WebTest (http://docs.pylonsproject.org/projects/webtest/) with Django's testing framework.
.. image:: https://img.shields.io/pypi/v/django-webtest.svg :target: https://pypi.python.org/pypi/django-webtest :alt: PyPI Version
.. image:: https://img.shields.io/github/license/kmike/django-webtest.svg :target: https://github.com/django-webtest/django-webtest/blob/master/LICENSE.txt :alt: License
.. image:: https://img.shields.io/travis/django-webtest/django-webtest/master.svg :target: http://travis-ci.org/django-webtest/django-webtest :alt: Build Status
django-webtest is an app for instant integration of Ian Bicking's WebTest (http://docs.pylonsproject.org/projects/webtest/) with Django's testing framework.
.. code-block:: console
$ pip install django-webtest
.. code-block:: python
from django_webtest import WebTest
class MyTestCase(WebTest):
# optional: we want some initial data to be able to login
fixtures = ['users', 'blog_posts']
# optional: default extra_environ for this TestCase
extra_environ = {'HTTP_ACCEPT_LANGUAGE': 'ru'}
def testBlog(self):
# pretend to be logged in as user `kmike` and go to the index page
index = self.app.get('/', user='kmike')
# All the webtest API is available. For example, we click
# on a <a href='/tech-blog/'>Blog</a> link, check that it
# works (result page doesn't raise exceptions and returns 200 http
# code) and test if result page have 'My Article' text in
# its body.
assert 'My Article' in index.click('Blog')
django-webtest provides a django.test.TestCase subclass
(django_webtest.WebTest
) that creates webtest.TestApp
around
django wsgi interface and makes it available in tests as self.app
.
It also features an optional user
argument for self.app.get
,
self.app.post
, etc. to help making authorized requests. This argument
should be a django.contrib.auth.models.User instance or a string with user's
username
for the user who is supposed to be logged in. To log out again,
call self.app.reset
, clearing all cookies. To make a bunch of calls
with the same user, call app.set_user(user)
before your requests; if
you want to disable that user, call app.get(..., user=None)
for one
request or app.set_user(None)
to unset the user for all following calls.
For 500 errors original traceback is shown instead of usual html result from handler500.
You also get the response.templates
and response.context
goodness that
is usually only available if you use Django's native test client. These
attributes contain a list of templates that were used to render the response
and the context used to render these templates. All of Django's native asserts (
assertFormError
, assertTemplateUsed
, assertTemplateNotUsed
,
assertContains
, assertNotContains
, assertRedirects
) are
also supported for WebTest responses.
The session dictionary is available via self.app.session
, and has the
same content than Django's native test client.
Unlike Django's native test client CSRF checks are not suppressed by default so missing CSRF tokens will cause test fails (and that's good).
If forms are submitted via WebTest forms API then all form fields (including CSRF token) are submitted automagically:
.. code-block:: python
class AuthTest(WebTest):
fixtures = ['users.json']
def test_login(self):
form = self.app.get(reverse('auth_login')).form
form['username'] = 'foo'
form['password'] = 'bar'
response = form.submit().follow()
self.assertEqual(response.context['user'].username, 'foo')
However if forms are submitted via raw POST requests using app.post
then
csrf tokens become hard to construct. CSRF checks can be disabled by setting
csrf_checks
attribute to False in this case:
.. code-block:: python
class MyTestCase(WebTest):
csrf_checks = False
def test_post(self):
self.app.post('/')
When a subclass of Django's TransactionTestCase
is desired,
use django_webtest.TransactionWebTest
.
For disabling CSRF checks in a pytest-django
fixture, see
Usage with PyTest
_.
All of these features can be easily set up manually (thanks to WebTest architecture) and they are even not neccessary for using WebTest with Django but it is nice to have some sort of integration instantly.
See http://docs.pylonsproject.org/projects/webtest/ for API help. Webtest can follow links, submit forms, parse html, xml and json responses with different parsing libraries, upload files and more.
If your project uses django-rest-framework__, the setting
REST_FRAMEWORK['AUTHENTICATION_CLASSES']
will be patched
automatically to include a class that links the rest-framework
authentication system with app.get(user=user)
.
.. __: https://www.django-rest-framework.org/
You need to install pytest-django <https://pytest-django.readthedocs.io>
_:
.. code-block:: console
$ pip install pytest-django
Then you can use django-webtest
's fixtures:
.. code-block:: python
def test_1(django_app):
resp = django_app.get('/')
assert resp.status_code == 200, 'Should return a 200 status code'
We have a django_app_factory
fixture we can use to create custom fixtures.
For example, one that doesn't do CSRF checks:
.. code-block:: python
# conftest.py
@pytest.fixture
def csrf_exempt_django_app(django_app_factory):
return django_app_factory(csrf_checks=False)
csrf_checks
and extra_environ
are the only arguments to
django_app_factory
.
While django.test.client.Client is fine for its purposes, it is not well-suited for functional or integration testing. From Django's test client docstring:
This is not intended as a replacement for Twill/Selenium or
the like - it is here to allow testing against the
contexts and templates produced by a view, rather than the
HTML rendered to the end-user.
WebTest plays on the same field as twill. WebTest has a nice API, is fast, small, talks to the django application via WSGI instead of HTTP and is an easy way to write functional/integration/acceptance tests. django-webtest is able to provide access to the names of rendered templates and template context just like native Django TestClient.
Development happens at github: https://github.com/django-webtest/django-webtest Issue tracker: https://github.com/django-webtest/django-webtest/issues
Feel free to submit ideas, bugs or pull requests.
Make sure tox
_ is installed and run:
.. code-block:: console
$ tox
from the source checkout.
.. _tox: http://tox.testrun.org
Add support for official Python & Django versions
Do not insert WebtestAuthentication to the head of DEFAULT_AUTHENTICATION_CLASSES.
Add Django 4 support
Remove Django 2 support
Update testing configurations for Django and Python as per Django documentation
Add some useful metadata for the project's PyPI listing
Minor changes to documentation
Update getting session in DjangoTestApp. Fixed #113
Remove py27/py35 support
Fix compatibility with django 3. See #96
Add integration with django-rest-framework auth
Add missing args to DjangoTestApp. Fixed #86
py34 and Django 1.8 are no longer tested (but may works)
allow to use positionnal args; fixed #89
remove deprecated pytest.yield_fixture functions. use pytest.fixture instead; fixed #88
Don't add duplicate WebtestUserMiddleware to the list of middlewares in WebTestMixin. fixed #87
restore MIDDLEWARE_CLASSES support; fixed #84
Passing user=None
to get/post/etc. methods will clear a user
previously set with set_user
instead of doing nothing.
Avoid sharing settings between tests in pytest plugin
Fix middleware settings name used
silence warnings about is_authenticated on 1.11
include correct hostname (testserver) when using set_cookie
Backward incompatibility: positionnal arguments are no longer supported. You'll need to replace them by keywords arguments.
Added support for Django 1.11
Dropped support for Django <= 1.7
Dropped support for Python 2.6
Changed value of HTTP_HOST
header from localhost
to testserver
, to
match behaviour of Django test client.
Fixed DjangoTestApp.options
Added DjangoTestApp.head
Added pytest fixtures
Fixed issue #40 - combining app.get
auto_follow=True
with other
keyword args.
Add compatibility to the MIDDLEWARE setting introduced in django 1.10
Drop support for django 1.2
Add set_user() to allow to set a user globally for the app
Allow 'click' to be given a user param
Mention testapp.reset() in readme
Allow to use json_
methods
xhr=True
feature (thanks Max Kharandziuk).TransactionWebTest
base class (thanks Julien Aubert).TransactionWebTest
base class is added (thanks Iurii Kriachko).self.app.put
and self.app.delete
methods (thanks
Ruslan Popov).self.app.session
.REMOTE_ADDR
is now '127.0.0.1'
by default. This is how
standard django's test client behave.
Please note that this can slow tests down and cause other side effects
if django-debug-toolbar 0.9.x is installed+configured and
INTERNAL_IPS
contain '127.0.0.1'
because debug toolbar will
become turned on during tests. The workaround is to remove
django-debug-toolbar middleware during tests in your test settings::
DEBUG_MIDDLEWARE = 'debug_toolbar.middleware.DebugToolbarMiddleware'
if DEBUG_MIDDLEWARE in MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES.remove(DEBUG_MIDDLEWARE)
self.renew_app()
method for resetting the 'browser' inside tests.response.template
and response.context
goodness (thanks Gregor Müllegger);get
and post
methods instead
of user's username.Initial release (thanks Ian Bicking for WebTest).