Skip to content

Best practices for back end testing

amplifi edited this page Jun 29, 2017 · 1 revision

How to test a View

Django Views are really hard to unit test, because they rely on many variables. The Django guides recommend to test views by sending a request using the test client; it's a good approach but it's also slowing down tests.

Here's a slightly alternative approach, which is quicker.

Test if URLs are linked to the correct views.

  1. Test if the URL name space reverses to the correct URL.
  2. Test if the URL is linked to the correct View. Simply compare the class names of the expected view and the one that was actually resolved.
# test_urls.py

def test_password_reset(self):
    # test if the URL name space reverses to the correct URL
    actual = reverse('accounts:password_reset')
    expected = '/account/password/reset/'
    self.assertEqual(actual, expected)

    # test if the URL is linked to the correct view
    resolved = resolve('/account/password/reset/')
    actual = resolved.func.__name__
    expected = views.PasswordReset.__name__
    
    self.assertEqual(actual, expected)

Testing a view's response

  1. Create a request using Django REST Frameworks APIRequestFactory
  2. Render the response using the View directly
  3. Run assertions against the response
def test_view(self)
    # create the request 
    request = APIRequestFactory().post('/account/activate/', {
        'uid': encode_uid(user.pk),
        'token': token
    })

    # Render the response using the view
    response = AccountVerify.as_view()(request).render()

    # Assert the response
    self.assertEqual(response.status_code, 200)
    self.assertIn('some content', response.content.decode("utf-8"))

Handling files

PR #514 changed the handling of files in tests: Files that were created during a test are always removed after the test. That way, we make sure that a file created in one test does not affect another test. For instance, there were cases where a test just passed because a file required for the test was still available from another test.

To make this possible, I created a pytest fixture that creates all necessary directories before a test and removes these directories after the test. (Here's how pytest fixtures work).

You should use the same fixture whenever you write a test that writes files to the file system.

from core.tests.util import make_dirs  # noqa

@pytest.mark.usefixtures('make_dirs')
class ResourcesTest(UserTestCase):
   def test_somthing(self):
       # write a bunch of files

Import make_dirs from core.tests.util and apply the fixture to a test class, which uses the file system by using the decorator @pytest.mark.usefixtures('make_dirs'). The fixture creates all directories required by FakeS3Storage by default. If for some reason, you need to write to a different directory, you can either create it inside the test:

from buckets.utils import ensure_dirs
from core.tests.util import make_dirs  # noqa

@pytest.mark.usefixtures('make_dirs')
class ResourcesTest(UserTestCase):
   def test_somthing(self):
       ensure_dirs('my/special_dir', 'another/special/dir')

Or you add the directory to the fixture itself if it is required in a significant number of different tests.

Some tests also write files to a temporary directory (temp), e.g. when an image is uploaded the image is downloaded from S3 and the post-processed to create the thumbnail. In this case, you should also add a fixture that removes the temp directory after the test:

from resources.tests.utils import clear_temp  # noqa

@pytest.mark.usefixtures('clear_temp')
class ResourcesTest(UserTestCase):
   def test_somthing(self):
       pass
Clone this wiki locally