Getting Started with _testing¶
Dependencies¶
This toolchain requires the responses
library.
globus_sdk._testing
is tested to operate with the latest version of
responses
.
Recommended Fixtures¶
Under pytest, this is the recommended fixture for setting up responses and guaranteeing that requests are sent to the production hostnames:
@pytest.fixture(autouse=True)
def mocked_responses(monkeypatch):
responses.start()
monkeypatch.setitem(os.environ, "GLOBUS_SDK_ENVIRONMENT", "production")
yield
responses.stop()
responses.reset()
Usage¶
Activating Individual Responses¶
Once responses
has been activated, each response fixture can be loaded and
activated by name:
from globus_sdk._testing import load_response
# load_response will add the response to `responses` and return it
load_response("auth.get_identities")
# "case" is used to have a single name map to multiple responses
data = load_response("auth.get_identities", case="multiple")
Responses can also be activated by passing an SDK client method, bound or unbound, as in:
import globus_sdk
from globus_sdk._testing import load_response
load_response(globus_sdk.AuthClient.get_identities)
load_response(globus_sdk.AuthClient.get_identities, case="unauthorized")
# or, with a bound method
ac = globus_sdk.AuthClient()
load_response(ac.get_identities, case="multiple")
Activating “Scenarios”¶
Some sets of fixtures may describe a scenario, and therefore it’s desirable to load all of them at once:
from globus_sdk._testing import load_response_set
fixtures = load_response_set("scenario.foo")
Getting Responses and ResponseSets without Activating¶
If you want to fetch a ResponseSet
or RegisteredResponse
without
activating it, you can do this via the get_response_set
method. Responses
must always be part of a response set, and the default name for an individual
response is "default"
.
from globus_sdk import AuthClient
from globus_sdk._testing import get_response_set
# rset will not be activated
rset = get_response_set(AuthClient.get_identities)
# you can get an individual response from rset
get_ids = rset.get("default")
# you can manually activate a whole set
rset.activate_all()
# or just one response from it by name
rset.activate("default")
Note that activating a whole response set may or may not make sense. For
example, the response set for AuthClient.get_identities
provides various
responses for the same API call.
Registering Response Sets¶
You can register your own response sets dynamically, and then load them up with
the same load_response_set
method. Note that custom response sets will
override the builtin response sets, if names match.
from globus_sdk._testing import load_response_set, register_response_set
import uuid
# register a scenario under which Globus Auth get_identities and Globus
# Transfer operation_ls both return payloads of `{"foo": "bar"}`
# use an autogenerated endpoint ID and put it into the response metadata
# register_response_set takes dict data and converts it to fixtures
endpoint_id = str(uuid.uuid1())
register_response_set(
"foobar",
{
"get_identities": {
"service": "auth",
"path": "/v2/api/identities",
"json": {"foo": "bar"},
},
"operation_ls": {
"service": "transfer",
"path": f"/operation/endpoint/{endpoint_id}/ls",
"json": {"foo": "bar"},
},
},
metadata={
"endpoint_id": endpoint_id,
},
)
# activate the result, and get it as a ResponseSet
fixtures = load_response_set("foobar")
# you can then pull the epid from the metadata
epid = fixtures.metadata["endpoint_id"]
transfer_client.operation_ls(epid)
register_response_set
can therefore be used to load fixture data early in
a tetstsuite run (e.g. as an autouse session-level fixture), for reference
later in the testsuite.
Loading Responses without Registering¶
Because RegisteredResponse
takes care of resolving "auth"
to the Auth
URL, "transfer"
to the Transfer URL, and so forth, you might want to use
globus_sdk._testing
in lieu of responses
even when registering single
responses for individual tests.
To support this mode of usage, load_response
can take a
RegisteredResponse
instance, and load_response_set
can take a
ResponseSet
instance.
Consider the following example of a parametrized test which uses
load_response(RegisteredResponse(...))
as a replacement for
responses.add
:
from globus_sdk._testing import load_response, RegisteredResponse
import pytest
@pytest.mark.parametrize("message", ["foo", "bar"])
def test_get_identities_sends_back_strange_message(message):
load_response(
RegisteredResponse(
service="auth",
path="/v2/api/identities",
json={"message": message},
)
)
ac = globus_sdk.AuthClient()
res = ac.get_identities(usernames="foo@example.com")
assert res["message"] == message
In this mode of usage, the response set registry is skipped altogether. It is not necessary to name or organize the response fixtures in a way that is usable outside of the specific test.
Using non-default responses.RequestsMock objects¶
By default, all methods in globus_sdk._testing
which converse with
responses
use the default mock. This is the behavior offered by
responses.add(...)
and similar methods.
However, you can pass a custom RequestsMock
if so desired to the following
methods:
get_last_request
load_response_set
load_response
as a keyword argument, requests_mock
.
e.g.
from globus_sdk._testing import get_last_request
import responses
custom_mock = responses.RequestsMock(...)
...
get_last_request(requests_mock=custom_mock)