responses icon indicating copy to clipboard operation
responses copied to clipboard

Cannot match empty query parameters

Open markhobson opened this issue 5 months ago • 1 comments

Describe the bug

The matcher query_param_matcher cannot match query parameters that have an empty string value.

Additional context

This is because RequestsMock._parse_request_params calls parse_qsl with the default argument keep_blank_values=False:

https://github.com/getsentry/responses/blob/cdd104d2852ef2c8c9eeabbe3ba56c4ca4e29e74/responses/init.py#L1054

This should be configurable, similar to allow_blank in urlencoded_params_matcher (see #533).

Version of responses

0.25.8

Steps to Reproduce

import responses
import requests
from responses import matchers


@responses.activate
def test_empty_query_param():
    responses.get("http://example.com/foo", match=[matchers.query_param_matcher({"bar": ""})], json={})

    response = requests.get("http://example.com/foo?bar=")

    assert response.status_code == 200

Expected Result

The test passes.

Actual Result

The test fails:

E           requests.exceptions.ConnectionError: Connection refused by Responses - the call doesn't match any registered mock.
E           
E           Request: 
E           - GET http://example.com/foo?bar=
E           
E           Available matches:
E           - GET http://example.com/foo Parameters do not match. {} doesn't match {'bar': ''}

markhobson avatar Aug 12 '25 10:08 markhobson

This can be worked around by using a custom RequestsMock:

class AllowEmptyQueryParamRequestsMock(RequestsMock):
    def _parse_request_params(self, url: str) -> dict[str, str | int | float | list[str | int | float | None]]:
        # Copy of RequestsMock._parse_request_params:
        params: dict[str, str | int | float | list[Any]] = {}
        # Allow empty query parameters
        for key, val in groupby(parse_qsl(urlsplit(url).query, keep_blank_values=True), lambda kv: kv[0]):
            values = list(map(lambda x: x[1], val))
            if len(values) == 1:
                values = values[0]  # type: ignore[assignment]
            params[key] = values
        return params

markhobson avatar Aug 12 '25 11:08 markhobson