ObjectDownloadView now inherits from SingleObjectMixin and BaseDownloadView. Added example for setup_view() in documentation.

This commit is contained in:
Benoît Bryon 2013-11-05 11:44:48 +01:00
parent 0371f84f26
commit 2c1ad3c730
15 changed files with 126 additions and 68 deletions

View file

@ -7,6 +7,7 @@ DATA_DIR = $(ROOT_DIR)/var
WGET = wget
PYTHON = $(shell which python)
PROJECT = $(shell $(PYTHON) -c "import setup; print setup.NAME")
PACKAGE = $(shell $(PYTHON) -c "import setup; print setup.PACKAGES[0]")
BUILDOUT_CFG = $(ROOT_DIR)/etc/buildout.cfg
BUILDOUT_DIR = $(ROOT_DIR)/lib/buildout
BUILDOUT_VERSION = 1.7.0
@ -51,17 +52,17 @@ test: test-app test-demo test-documentation
test-app:
$(NOSE) -c $(ROOT_DIR)/etc/nose.cfg --with-coverage --cover-package=django_downloadview django_downloadview tests
$(NOSE) -c $(ROOT_DIR)/etc/nose/base.cfg -c $(ROOT_DIR)/etc/nose/$(PACKAGE).cfg
mv $(ROOT_DIR)/.coverage $(ROOT_DIR)/var/test/app.coverage
test-demo:
$(BIN_DIR)/demo test demo
$(BIN_DIR)/demo test --nose-verbosity=2
mv $(ROOT_DIR)/.coverage $(ROOT_DIR)/var/test/demo.coverage
test-documentation:
$(NOSE) -c $(ROOT_DIR)/etc/nose.cfg sphinxcontrib.testbuild.tests
$(NOSE) -c $(ROOT_DIR)/etc/nose/base.cfg sphinxcontrib.testbuild.tests
sphinx:

View file

@ -8,6 +8,7 @@ demoproject_dir = dirname(abspath(__file__))
demo_dir = dirname(demoproject_dir)
root_dir = dirname(demo_dir)
data_dir = join(root_dir, 'var')
cfg_dir = join(root_dir, 'etc')
# Mandatory settings.
@ -76,21 +77,10 @@ DOWNLOADVIEW_MIDDLEWARES = (
)
# Development configuration.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = ['--verbose',
'--nocapture',
'--rednose',
'--with-id', # allows --failed which only reruns failed tests
'--id-file=%s' % join(data_dir, 'test', 'noseids'),
'--with-doctest',
'--with-xunit',
'--xunit-file=%s' % join(data_dir, 'test', 'nosetests.xml'),
'--with-coverage',
'--cover-erase',
'--cover-package=django_downloadview',
'--no-path-adjustment',
'--all-modules',
]
nose_cfg_dir = join(cfg_dir, 'nose')
NOSE_ARGS = ['--config={etc}/base.cfg'.format(etc=nose_cfg_dir),
'--config={etc}/{package}.cfg'.format(etc=nose_cfg_dir,
package=__package__)]

View file

@ -1,15 +1,13 @@
try:
from unittest import mock
except ImportError: # Python 2.x fallback.
import mock
import unittest
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
import django.test
from django_downloadview import temporary_media_root, assert_download_response
from django_downloadview import assert_download_response, temporary_media_root
from django_downloadview import setup_view
from demoproject.storage.views import storage
from demoproject.storage import views
# Fixtures.
@ -17,13 +15,13 @@ file_content = 'Hello world!\n'
def setup_file(path):
storage.save(path, ContentFile(file_content))
views.storage.save(path, ContentFile(file_content))
class StaticPathTestCase(django.test.TestCase):
@temporary_media_root()
def test_download_response(self):
"""'static_path' streams file by path."""
"""'storage:static_path' streams file by path."""
setup_file('1.txt')
url = reverse('storage:static_path', kwargs={'path': '1.txt'})
response = self.client.get(url)
@ -34,10 +32,20 @@ class StaticPathTestCase(django.test.TestCase):
mime_type='text/plain')
class DynamicPathTestCase(django.test.TestCase):
class DynamicPathIntegrationTestCase(django.test.TestCase):
"""Integration tests around ``storage:dynamic_path`` URL."""
@temporary_media_root()
def test_download_response(self):
"""'dynamic_path' streams file by generated path."""
"""'dynamic_path' streams file by generated path.
As we use ``self.client``, this test involves the whole Django stack,
including settings, middlewares, decorators... So we need to setup a
file, the storage, and an URL.
This test actually asserts the URL ``storage:dynamic_path`` streams a
file in storage.
"""
setup_file('1.TXT')
url = reverse('storage:dynamic_path', kwargs={'path': '1.txt'})
response = self.client.get(url)
@ -46,3 +54,23 @@ class DynamicPathTestCase(django.test.TestCase):
content=file_content,
basename='1.TXT',
mime_type='text/plain')
class DynamicPathUnitTestCase(unittest.TestCase):
"""Unit tests around ``views.DynamicStorageDownloadView``."""
def test_get_path(self):
"""DynamicStorageDownloadView.get_path() returns uppercase path.
Uses :func:`~django_downloadview.test.setup_view` to target only
overriden methods.
This test does not involve URLconf, middlewares or decorators. It is
fast. It has clear scope. It does not assert ``storage:dynamic_path``
URL works. It targets only custom ``DynamicStorageDownloadView`` class.
"""
view = setup_view(views.DynamicStorageDownloadView(),
django.test.RequestFactory().get('/fake-url'),
path='dummy path')
path = view.get_path()
self.assertEqual(path, 'DUMMY PATH')

View file

@ -18,4 +18,5 @@ from django_downloadview.views import (PathDownloadView, # NoQA
DownloadMixin)
from django_downloadview.sendfile import sendfile # NoQA
from django_downloadview.test import (assert_download_response, # NoQA
setup_view,
temporary_media_root)

View file

@ -8,6 +8,34 @@ from django.test.utils import override_settings
from django_downloadview.middlewares import is_download_response
def setup_view(view, request, *args, **kwargs):
"""Mimic ``as_view()``, but returns view instance.
Use this function to get view instances on which you can run unit tests,
by testing specific methods.
This is an early implementation of
https://code.djangoproject.com/ticket/20456
``view``
A view instance, such as ``TemplateView(template_name='dummy.html')``.
Initialization arguments are the same you would pass to ``as_view()``.
``request``
A request object, typically built with
:class:`~django.test.client.RequestFactory`.
``args`` and ``kwargs``
"URLconf" positional and keyword arguments, the same you would pass to
:func:`~django.core.urlresolvers.reverse`.
"""
view.request = request
view.args = args
view.kwargs = kwargs
return view
class temporary_media_root(override_settings):
"""Temporarily override settings.MEDIA_ROOT with a temporary directory.

View file

@ -1,18 +1,2 @@
# -*- coding: utf-8 -*-
"""Unit tests."""
def setup_view(view, request, *args, **kwargs):
"""Mimic as_view() returned callable, but returns view instance.
``args`` and ``kwargs`` are the same you would pass to
:func:`~django.core.urlresolvers.reverse`.
This is an early implementation of
https://code.djangoproject.com/ticket/20456
"""
view.request = request
view.args = args
view.kwargs = kwargs
return view

View file

@ -54,6 +54,7 @@ class APITestCase(unittest.TestCase):
'DownloadDispatcherMiddleware',
# Testing:
'assert_download_response',
'setup_view',
'temporary_media_root',
# Utilities:
'StringIteratorIO',

View file

@ -9,7 +9,7 @@ except ImportError:
from django.http.response import HttpResponseNotModified
import django.test
from django_downloadview.tests import setup_view
from django_downloadview.test import setup_view
from django_downloadview.views import base

View file

@ -66,19 +66,19 @@ class DownloadMixin(object):
else:
return was_modified_since(since, modification_time, size)
def not_modified_response(self, **response_kwargs):
def not_modified_response(self, *response_args, **response_kwargs):
"""Return :class:`django.http.HttpResponseNotModified` instance."""
return HttpResponseNotModified(**response_kwargs)
return HttpResponseNotModified(*response_args, **response_kwargs)
def download_response(self, **response_kwargs):
def download_response(self, *response_args, **response_kwargs):
"""Return :class:`~django_downloadview.response.DownloadResponse`."""
response_kwargs.setdefault('file_instance', self.file_instance)
response_kwargs.setdefault('attachment', self.attachment)
response_kwargs.setdefault('basename', self.get_basename())
response = self.response_class(**response_kwargs)
response = self.response_class(*response_args, **response_kwargs)
return response
def render_to_response(self, **response_kwargs):
def render_to_response(self, *response_args, **response_kwargs):
"""Return "download" response.
Respects the "HTTP_IF_MODIFIED_SINCE" header if any. In that case, uses
@ -94,7 +94,7 @@ class DownloadMixin(object):
if not self.was_modified_since(self.file_instance, since):
return self.not_modified_response(**response_kwargs)
# Return download response.
return self.download_response(**response_kwargs)
return self.download_response(*response_args, **response_kwargs)
class BaseDownloadView(DownloadMixin, View):

View file

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
"""Stream files that live in models."""
from django.views.generic.detail import BaseDetailView
from django.views.generic.detail import SingleObjectMixin
from django_downloadview.views.base import DownloadMixin
from django_downloadview.views.base import BaseDownloadView
class ObjectDownloadView(DownloadMixin, BaseDetailView):
class ObjectDownloadView(SingleObjectMixin, BaseDownloadView):
"""Serve file fields from models.
This class extends BaseDetailView, so you can use its arguments to target
@ -76,3 +76,7 @@ class ObjectDownloadView(DownloadMixin, BaseDetailView):
if model_field:
basename = getattr(self.object, model_field)
return basename
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(ObjectDownloadView, self).get(request, *args, **kwargs)

View file

@ -4,18 +4,12 @@ Write tests
`django_downloadview` embeds test utilities:
* :func:`~django_downloadview.test.assert_download_response`
* :func:`~django_downloadview.test.temporary_media_root`
* :func:`~django_downloadview.test.assert_download_response`
* :func:`~django_downloadview.test.setup_view`
* :func:`~django_downloadview.nginx.tests.assert_x_accel_redirect`
************************
assert_download_response
************************
.. autofunction:: django_downloadview.test.assert_download_response
********************
temporary_media_root
********************
@ -23,11 +17,28 @@ temporary_media_root
.. autofunction:: django_downloadview.test.temporary_media_root
*******
Example
*******
************************
assert_download_response
************************
Here are the tests related to :doc:`StorageDownloadView demo </views/storage>`:
.. autofunction:: django_downloadview.test.assert_download_response
Examples, related to :doc:`StorageDownloadView demo </views/storage>`:
.. literalinclude:: /../demo/demoproject/storage/tests.py
:language: python
:lines: 3-7, 9-57
**********
setup_view
**********
.. autofunction:: django_downloadview.test.setup_view
Example, related to :doc:`StorageDownloadView demo </views/storage>`:
.. literalinclude:: /../demo/demoproject/storage/tests.py
:language: python
:lines: 1-2, 8-12, 59-

View file

@ -70,12 +70,12 @@ collective.recipe.omelette = 0.16
coverage = 3.6
distribute = 0.6.34
Django = 1.5
django-nose = 1.1
django-nose = 1.2
docutils = 0.10
evg.recipe.activate = 0.5
Jinja2 = 2.6
mock = 1.0.1
nose = 1.2.1
nose = 1.3.0
Pygments = 1.6
python-termstyle = 0.1.10
rednose = 0.3

View file

@ -5,3 +5,5 @@ with-doctest = True
rednose = True
no-path-adjustment = True
all-modules = True
cover-inclusive = True
cover-tests = True

4
etc/nose/demoproject.cfg Normal file
View file

@ -0,0 +1,4 @@
[nosetests]
with-coverage = True
cover-package = demoproject
tests = demoproject

View file

@ -0,0 +1,4 @@
[nosetests]
with-coverage = True
cover-package = django_downloadview
tests = django_downloadview,tests