Skip to content

Instantly share code, notes, and snippets.

@mjtamlyn
Last active May 25, 2016 15:15
Show Gist options
  • Save mjtamlyn/96edb025e01c79a47bc3012f79c5444f to your computer and use it in GitHub Desktop.
Save mjtamlyn/96edb025e01c79a47bc3012f79c5444f to your computer and use it in GitHub Desktop.
Utilities for testing graphene APIs

Some prototype utilities for testing projects built with graphene.

Context

Simple utility class for creating context objects (e.g. context=Context(user=user))

Fragment

A wrapper class for strings to say "don't touch me". Can be used for arguments and as keys of the object passed to build_query.

GrapheneTestCase

A unittest.TestCase mixin with utility functions

_cast_dict

Recursively un-orders dicts so that the error diffs print nicely

_encode_arg

Encodes arguments to be passed to query builder. Understands strings, lists, and Fragments.

build_query(data, arguments=None)

Builds a GraphQL query from the shape of the expected data. Understands nested dicts, (homogeneous) lists, and fragments. Can be passed arguments, a dict mapping keys (anywhere in the tree) to a dict of arguments.

connection(ids)

Relay utility for wrapping a set of ids into a connection shape

assertQuerySuccess(query, expected, context=None)

Executes a query and verifies it matches the expected data. Tries to give helpful output.

assertQueryComplete(expected, node, schema)

Verifies that a given expected data query exercises the entire node definition (at least in terms of covering all keys).

class Context(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class Fragment(object):
def __init__(self, fragment):
self.fragment = fragment
def __str__(self):
return self.fragment
class GrapheneTestCase(object):
def _cast_dict(self, d):
"""Recursively cast and ordered dict to an unordered dict.
This makes unittest errors more informative.
"""
if d is None:
return d
d = dict(d)
for k, v in d.items():
if isinstance(v, collections.OrderedDict):
d[k] = self._cast_dict(v)
elif isinstance(v, list):
d[k] = map(lambda x: self._cast_dict(x) if isinstance(x, collections.OrderedDict) else x, v)
return d
def _encode_arg(self, arg):
if isinstance(arg, Fragment):
return arg
elif isinstance(arg, six.string_types):
return '"%s"' % arg
elif isinstance(arg, list):
return '[%s]' % ', '.join(self._encode_arg(a) for a in arg)
return arg
def build_query(self, d, arguments=None):
"""Build a GraphQL query fragment from the shape of the expected data."""
if arguments is None:
arguments = {}
query = []
for k, v in d.items():
if arguments.get(k):
local_args = ['%s:%s' % (arg_k, self._encode_arg(arg_v)) for arg_k, arg_v in arguments[k].items()]
k = '%s(%s)' % (k, ', '.join(local_args))
if isinstance(v, Fragment):
subquery = v
elif isinstance(v, dict):
subquery = self.build_query(v, arguments)
elif isinstance(v, list):
subquery = self.build_query(v[0], arguments)
else:
subquery = ''
if subquery:
query.append('%s %s' % (k, subquery))
else:
query.append(k)
return '{ %s }' % ' '.join(query)
def connection(self, ids):
return {
'edges': [{'node': {'id': id}} for id in ids]
}
def assertQuerySuccess(self, query, expected, context=None):
result = schema.execute(query, context_value=context)
return_data = self._cast_dict(result.data)
if result.errors:
msg = 'Query failed: %s' % result.errors
else:
msg = None
self.assertEqual(return_data, expected, msg)
def assertQueryComplete(self, expected, node, schema):
available_node_keys = set(node.internal_type(schema).get_fields().keys())
queried_keys = set(expected.keys())
self.assertEqual(
available_node_keys,
queried_keys,
'Test query did not cover all available fields.\nMissing: %s' % (
', '.join(available_node_keys - queried_keys)
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment