0

I have a few Django Rest Framework API endpoints I want to test.

They are behind an authenticated page. I'm trying to use the DRF RequestsClient to do that.

My code looks like this:

from rest_framework.test import APITestCase
from rest_framework.test import RequestsClient
from django.urls import reverse
import requests

URL_PREFIX="http://testserver"
API_TOKEN="tfhgffhgf675h"

class APITests(APITestCase):
    @staticmethod
    def _get_full_url(url, *args, **kwargs):
        return URL_PREFIX + reverse(url, *args, **kwargs)

    def setUp(self):
        self.client = RequestsClient()

    def test_stuff(self):
        url = self._get_full_url("ahs:agena_results-list")

        # Raw requests library - works fine
        # Note: Testing shows that the RequestsClient() seems to do some sort of magic to
        # be able to resolve http://testserver. A raw requests GET request must hit it at
        # 127.0.0.1, and specify the port
        response = requests.get("http://127.0.0.1:8000", headers={"Authorization": f"Token {API_TOKEN}"})

        # RequestsClient() - doesn't work
        response = self.client.get(url, headers={"Authorization": f"Token {API_TOKEN}"})

My RequestsClient request doesn't work, but my raw requests request works fine.

The exception raised when using the RequestsClient is pretty cryptic:

Internal Server Error: /ahs/api/agena_results/
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1361, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 83, in _execute
    self.db.validate_no_broken_transaction()
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 480, in validate_no_broken_transaction
    raise TransactionManagementError(
django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 497, in dispatch
    self.initial(request, *args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 414, in initial
    self.perform_authentication(request)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 324, in perform_authentication
    request.user
  File "/usr/local/lib/python3.9/site-packages/rest_framework/request.py", line 227, in user
    self._authenticate()
  File "/usr/local/lib/python3.9/site-packages/rest_framework/request.py", line 380, in _authenticate
    user_auth_tuple = authenticator.authenticate(self)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/authentication.py", line 196, in authenticate
    return self.authenticate_credentials(token)
  File "/usr/local/lib/python3.9/site-packages/rest_framework/authentication.py", line 201, in authenticate_credentials
    token = model.objects.select_related('user').get(key=key)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 492, in get
    num = len(clone)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 302, in __len__
    self._fetch_all()
  File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 1507, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 57, in __iter__
    results = compiler.execute_sql(
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1364, in execute_sql
    cursor.close()
  File "/usr/local/lib/python3.9/site-packages/MySQLdb/cursors.py", line 83, in close
    while self.nextset():
  File "/usr/local/lib/python3.9/site-packages/MySQLdb/cursors.py", line 137, in nextset
    nr = db.next_result()
MySQLdb._exceptions.OperationalError: (2006, '')
.
----------------------------------------------------------------------
Ran 1 test in 30.774s

The strange thing is that if I remove the Authorization header from the RequestsClient request, it doesn't crash, but does return a 403, so that doesn't help me.

I also tried setting the Authorization header on the RequestsClient this way, but that didn't help either:

self.client.headers.update({'Authorization': f'Token {API_TOKEN}'})

Any idea what I'm doing wrong?

John
  • 2,551
  • 3
  • 30
  • 55
  • duplicate question https://stackoverflow.com/questions/21458387/transactionmanagementerror-you-cant-execute-queries-until-the-end-of-the-atom – Pfinnn Jun 24 '22 at 13:12
  • Does this answer your question? [TransactionManagementError "You can't execute queries until the end of the 'atomic' block" while using signals, but only during Unit Testing](https://stackoverflow.com/questions/21458387/transactionmanagementerror-you-cant-execute-queries-until-the-end-of-the-atom) – Pfinnn Jun 24 '22 at 13:13
  • @Pfinnn thanks for the link. I came across that one before I posted. Most of the answers are close to 10 years old, and wrapping my request in a `transaction.atomic` didn't help. Is there something else I can try? – John Jun 24 '22 at 14:01
  • there are many answers on that thread. Did you read them? The accepted answer suggests wrapping the transaction in with.transaction.atomic(): Also read here, maybe this will help: https://docs.djangoproject.com/en/4.0/topics/db/transactions/ – Pfinnn Jun 24 '22 at 14:16
  • @Pfinnn `transaction.atomic` (from the top accepted answer) didn't help. Using `APITransactionTestCase` (from the second top accepted answer) didn't help. Not surprising since both answers are really, really, old. Every other answer appears to be just guessing at what it might be. Is there something specific I can try? – John Jun 24 '22 at 19:01

1 Answers1

0

Using APILiveServerTestCase instead of APITestCase when using RequestsClient solves the problem.

Apparently, the poor documentation around this has been known for some time now, but does not appear to have improved since then.

John
  • 2,551
  • 3
  • 30
  • 55