smhk

Tastypie and pytest

Tastypie offers a ResourceTestCaseMixin which can be inherited in conjunction with the Django TestCase to allow unit testing Tastypie endpoints in a unittest style manner. However if wanting to use pytest instead of unittest in order to take advantages of its features such as fixtures, I found a different approach had to be taken.

Although pytest supports using fixtures inside classes, I could not get them to work in conjunction with the Tastypie ResourceTestCaseMixin. The solution I found was to avoid using ResourceTestCaseMixin and Django’s TestCase entirely, and instead use only top level functions with pytest decorators, and fixtures that pull in the necessary extras.

conftest.py
import pytest
from tastypie.test import TestApiClient
from tastypie.serializer import Serializer
from tastypie.models import ApiKey
from django.contrib.auth.models import User


@pytest.fixture(scope='function'):
def api_client():
    return TestApiClient()


@pytest.fixture(scope='function'):
def serializer():
    return Serializer()


@pytest.fixture(scope='function'):
def auth():
    username = 'username'
    password = 'password'
    email = 'hello@somewhere.com'

    user = User.objects.create_user(username, email, password)
    user.save()

    api_key, _ = ApiKey.objects.get_or_create(user=user)
    api_key.save()

    # Taken from `ResourceTestCaseMixin().create_apikey`, which unfortunately is
    # not a static method even though it does not use `self`, otherwise we could
    # just call it directly from here.
    credentials = 'ApiKey %s:%s' % (username, api_key.key)

    return {
        'user': user,
        'api_key': api_key,
        'credentials': credentials,
    }
test_endpoint.py
import pytest


@pytest.mark.django_db
def test_authentication_failure(api_client):
    """Test that an unauthenticated response is rejected
    """
    response = api_client.get(
        '/api/v1/my-endpoint/',
        format='json',
        authentication=None,
    )
    assert response.status_code == 401


@pytest.mark.django_db
def test_authentication_success(api_client, auth, serializer):
    """Test that a correctly authenticated response is accepted
    """
    response = api_client.get(
        '/api/v1/my-endpoint/',
        format='json',
        authentication=auth['credentials'],
    )
    assert response.status_code == 200

    payload = serializer.deserialize(
        response.content,
        format=response['Content-Type'],
    )

    # TODO: Assert payload contents are what was expected

The create_apikey method which was copied from Tastypie can be found here.