This is an issue when using Starlette or FastAPI’s TestClient with the data parameter to send raw bytes or text strings
in HTTP requests. The data parameter should only be used for form data (dictionaries), while raw content should use the
content parameter.
Starlette version 0.21.0 introduced a breaking change by replacing the underlying HTTP client from requests to httpx.
These two libraries have different parameter conventions for sending request bodies.
In the requests library, the data parameter accepts various types including bytes, strings, and dictionaries. However, in
httpx, the parameter naming is more specific:
data parameter is reserved for form data (dictionaries that will be encoded as application/x-www-form-urlencoded)
content parameter should be used for raw bytes or text strings When you use data with bytes or text in an httpx-based TestClient, the request may not behave as expected. The library
might attempt to process the raw content as form data, leading to incorrect encoding or Content-Type headers.
This issue commonly appears when upgrading Starlette or FastAPI applications, causing previously working tests to fail or behave unexpectedly. The parameter mismatch can result in:
Using the wrong parameter can cause tests to fail or pass incorrectly, masking real issues in the application code. This reduces the reliability of the test suite and may allow bugs to reach production.
The incorrect parameter usage may also cause confusion during debugging, as the test behavior differs from what the code suggests.
Replace the data parameter with content when passing bytes to a TestClient request method. This ensures the raw bytes are
sent correctly as the request body.
from starlette.testclient import TestClient
from starlette.applications import Starlette
app = Starlette()
client = TestClient(app)
response = client.post('/api/endpoint', data=b'raw bytes') # Noncompliant
from starlette.testclient import TestClient
from starlette.applications import Starlette
app = Starlette()
client = TestClient(app)
response = client.post('/api/endpoint', content=b'raw bytes')
Replace the data parameter with content when passing text strings to a TestClient request method. This ensures the text
is sent correctly as the request body.
from starlette.testclient import TestClient
from starlette.applications import Starlette
app = Starlette()
client = TestClient(app)
response = client.put('/api/resource', data='text content') # Noncompliant
from starlette.testclient import TestClient
from starlette.applications import Starlette
app = Starlette()
client = TestClient(app)
response = client.put('/api/resource', content='text content')
Replace the data parameter with content when passing bytes to a FastAPI TestClient request. This is necessary because
FastAPI’s TestClient uses httpx, which requires the content parameter for raw bytes.
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
client = TestClient(app)
response = client.post('/upload', data=b'\x89PNG\r\n\x1a\n') # Noncompliant
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
client = TestClient(app)
response = client.post('/upload', content=b'\x89PNG\r\n\x1a\n')
Replace the data parameter with content when passing JSON strings to a FastAPI TestClient request. Note that for JSON
data, you should typically use the json parameter instead, but if you’re sending a pre-serialized JSON string, use
content.
from fastapi import FastAPI
from fastapi.testclient import TestClient
import json
app = FastAPI()
client = TestClient(app)
payload = json.dumps({"key": "value"})
response = client.post('/api/data', data=payload) # Noncompliant
from fastapi import FastAPI
from fastapi.testclient import TestClient
import json
app = FastAPI()
client = TestClient(app)
payload = json.dumps({"key": "value"})
response = client.post('/api/data', content=payload)