Skip to content

Commit

Permalink
Add infrastructure for test-helper methods (#773)
Browse files Browse the repository at this point in the history
  • Loading branch information
pakrym-stripe authored Mar 25, 2022
1 parent 5b0c253 commit 3c7be4b
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ venv.bak/
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/.idea
5 changes: 5 additions & 0 deletions stripe/api_resources/abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@

from stripe.api_resources.abstract.custom_method import custom_method

from stripe.api_resources.abstract.test_helpers import (
test_helpers,
APIResourceTestHelpers,
)

from stripe.api_resources.abstract.nested_resource_class_methods import (
nested_resource_class_methods,
)
77 changes: 77 additions & 0 deletions stripe/api_resources/abstract/test_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from __future__ import absolute_import, division, print_function

from stripe import error, util, six
from stripe.six.moves.urllib.parse import quote_plus
from stripe.api_resources.abstract import APIResource


class APIResourceTestHelpers:
"""
The base type for the TestHelper nested classes.
Handles request URL generation for test_helper custom methods.
Should be used in combination with the @test_helpers decorator.
@test_helpers
class Foo(APIResource):
class TestHelpers(APIResourceTestHelpers):
"""

def __init__(self, resource):
self.resource = resource

@classmethod
def class_url(cls):
if cls == APIResourceTestHelpers:
raise NotImplementedError(
"APIResourceTestHelpers is an abstract class. You should perform "
"actions on its subclasses (e.g. Charge, Customer)"
)
# Namespaces are separated in object names with periods (.) and in URLs
# with forward slashes (/), so replace the former with the latter.
base = cls._resource_cls.OBJECT_NAME.replace(".", "/")
return "/v1/test_helpers/%ss" % (base,)

def instance_url(self):
id = self.resource.get("id")

if not isinstance(id, six.string_types):
raise error.InvalidRequestError(
"Could not determine which URL to request: %s instance "
"has invalid ID: %r, %s. ID should be of type `str` (or"
" `unicode`)" % (type(self).__name__, id, type(id)),
"id",
)

id = util.utf8(id)
base = self.class_url()
extn = quote_plus(id)
return "%s/%s" % (base, extn)


def test_helpers(cls):
"""
test_helpers decorator adds a test_helpers property and
wires the parent resource class to the nested TestHelpers class.
Should only be used on types that inherit from APIResource.
@test_helpers
class Foo(APIResource):
class TestHelpers(APIResourceTestHelpers):
"""

def test_helpers_getter(self):
return self.TestHelpers(self)

if not issubclass(cls, APIResource):
raise ValueError(
"Could not apply @test_helpers decorator to %r."
" The class should a subclass of APIResource." % cls
)

cls.TestHelpers._resource_cls = cls
cls.TestHelpers._static_request = cls._static_request
cls.TestHelpers._static_request_stream = cls._static_request_stream

cls.test_helpers = property(test_helpers_getter)
return cls
66 changes: 66 additions & 0 deletions tests/api_resources/abstract/test_test_helpers_api_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import absolute_import, division, print_function

import stripe
import pytest
from stripe import util
from stripe.api_resources.abstract import APIResourceTestHelpers


class TestTestHelperAPIResource(object):
@stripe.api_resources.abstract.test_helpers
class MyTestHelpersResource(stripe.api_resources.abstract.APIResource):
OBJECT_NAME = "myresource"

@stripe.api_resources.abstract.custom_method(
"do_stuff", http_verb="post", http_path="do_the_thing"
)
class TestHelpers(APIResourceTestHelpers):
def __init__(self, resource):
self.resource = resource

def do_stuff(self, idempotency_key=None, **params):
url = self.instance_url() + "/do_the_thing"
headers = util.populate_headers(idempotency_key)
self.resource.refresh_from(
self.resource.request("post", url, params, headers)
)
return self.resource

def test_call_custom_method_class(self, request_mock):
request_mock.stub_request(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"id": "mid", "thing_done": True},
rheaders={"request-id": "req_id"},
)

obj = self.MyTestHelpersResource.TestHelpers.do_stuff("mid", foo="bar")

request_mock.assert_requested(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"foo": "bar"},
)
assert obj.thing_done is True

def test_call_custom_method_instance_via_property(self, request_mock):
request_mock.stub_request(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"id": "mid", "thing_done": True},
rheaders={"request-id": "req_id"},
)

obj = self.MyTestHelpersResource.construct_from({"id": "mid"}, "mykey")
obj.test_helpers.do_stuff(foo="bar")

request_mock.assert_requested(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"foo": "bar"},
)
assert obj.thing_done is True

def test_helper_decorator_raises_for_non_resource(self):
with pytest.raises(ValueError):
stripe.api_resources.abstract.test_helpers(str)
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ deps =
pytest-cov >= 2.8.1, < 2.11.0
pytest-mock >= 2.0.0
pytest-xdist >= 1.31.0
commands = pytest --cov {posargs:-n auto}
# ignore stripe directory as all tests are inside ./tests
commands = pytest --cov {posargs:-n auto} --ignore stripe
# compilation flags can be useful when prebuilt wheels cannot be used, e.g.
# PyPy 2 needs to compile the `cryptography` module. On macOS this can be done
# by passing the following flags:
Expand Down

0 comments on commit 3c7be4b

Please sign in to comment.