From dfcd7f256340301d7551e2af383e4e84454db097 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sun, 9 Apr 2017 21:04:03 -0700 Subject: [PATCH 1/6] =?UTF-8?q?First=20version=20of=20the=20Graphene=20tes?= =?UTF-8?q?t=20client=20and=20snapshots=20=F0=9F=92=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/starwars/tests/snapshots/__init__.py | 0 .../tests/snapshots/snap_test_query.py | 205 ++++++++++++++++ examples/starwars/tests/test_query.py | 219 +++--------------- .../tests/snapshots/__init__.py | 0 .../tests/snapshots/snap_test_connections.py | 35 +++ .../tests/snapshots/snap_test_mutation.py | 65 ++++++ .../snap_test_objectidentification.py | 56 +++++ .../starwars_relay/tests/test_connections.py | 30 +-- .../starwars_relay/tests/test_mutation.py | 54 +---- .../tests/test_objectidentification.py | 63 ++--- graphene/test/__init__.py | 28 +++ setup.py | 1 + 12 files changed, 442 insertions(+), 314 deletions(-) create mode 100644 examples/starwars/tests/snapshots/__init__.py create mode 100644 examples/starwars/tests/snapshots/snap_test_query.py create mode 100644 examples/starwars_relay/tests/snapshots/__init__.py create mode 100644 examples/starwars_relay/tests/snapshots/snap_test_connections.py create mode 100644 examples/starwars_relay/tests/snapshots/snap_test_mutation.py create mode 100644 examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py create mode 100644 graphene/test/__init__.py diff --git a/examples/starwars/tests/snapshots/__init__.py b/examples/starwars/tests/snapshots/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/starwars/tests/snapshots/snap_test_query.py b/examples/starwars/tests/snapshots/snap_test_query.py new file mode 100644 index 00000000..63c097d4 --- /dev/null +++ b/examples/starwars/tests/snapshots/snap_test_query.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- + +# snapshottest: v1 +# https://pypi.python.org/pypi/snapshottest + +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_hero_name_query 1'] = { + 'data': { + 'hero': { + 'name': 'R2-D2' + } + } +} + +snapshots['test_hero_name_and_friends_query 1'] = { + 'data': { + 'hero': { + 'id': '2001', + 'name': 'R2-D2', + 'friends': [ + { + 'name': 'Luke Skywalker' + }, + { + 'name': 'Han Solo' + }, + { + 'name': 'Leia Organa' + } + ] + } + } +} + +snapshots['test_nested_query 1'] = { + 'data': { + 'hero': { + 'name': 'R2-D2', + 'friends': [ + { + 'name': 'Luke Skywalker', + 'appearsIn': [ + 'NEWHOPE', + 'EMPIRE', + 'JEDI' + ], + 'friends': [ + { + 'name': 'Han Solo' + }, + { + 'name': 'Leia Organa' + }, + { + 'name': 'C-3PO' + }, + { + 'name': 'R2-D2' + } + ] + }, + { + 'name': 'Han Solo', + 'appearsIn': [ + 'NEWHOPE', + 'EMPIRE', + 'JEDI' + ], + 'friends': [ + { + 'name': 'Luke Skywalker' + }, + { + 'name': 'Leia Organa' + }, + { + 'name': 'R2-D2' + } + ] + }, + { + 'name': 'Leia Organa', + 'appearsIn': [ + 'NEWHOPE', + 'EMPIRE', + 'JEDI' + ], + 'friends': [ + { + 'name': 'Luke Skywalker' + }, + { + 'name': 'Han Solo' + }, + { + 'name': 'C-3PO' + }, + { + 'name': 'R2-D2' + } + ] + } + ] + } + } +} + +snapshots['test_fetch_luke_query 1'] = { + 'data': { + 'human': { + 'name': 'Luke Skywalker' + } + } +} + +snapshots['test_fetch_some_id_query 1'] = { + 'data': { + 'human': { + 'name': 'Luke Skywalker' + } + } +} + +snapshots['test_fetch_some_id_query2 1'] = { + 'data': { + 'human': { + 'name': 'Han Solo' + } + } +} + +snapshots['test_invalid_id_query 1'] = { + 'data': { + 'human': None + } +} + +snapshots['test_fetch_luke_aliased 1'] = { + 'data': { + 'luke': { + 'name': 'Luke Skywalker' + } + } +} + +snapshots['test_fetch_luke_and_leia_aliased 1'] = { + 'data': { + 'luke': { + 'name': 'Luke Skywalker' + }, + 'leia': { + 'name': 'Leia Organa' + } + } +} + +snapshots['test_duplicate_fields 1'] = { + 'data': { + 'luke': { + 'name': 'Luke Skywalker', + 'homePlanet': 'Tatooine' + }, + 'leia': { + 'name': 'Leia Organa', + 'homePlanet': 'Alderaan' + } + } +} + +snapshots['test_use_fragment 1'] = { + 'data': { + 'luke': { + 'name': 'Luke Skywalker', + 'homePlanet': 'Tatooine' + }, + 'leia': { + 'name': 'Leia Organa', + 'homePlanet': 'Alderaan' + } + } +} + +snapshots['test_check_type_of_r2 1'] = { + 'data': { + 'hero': { + '__typename': 'Droid', + 'name': 'R2-D2' + } + } +} + +snapshots['test_check_type_of_luke 1'] = { + 'data': { + 'hero': { + '__typename': 'Human', + 'name': 'Luke Skywalker' + } + } +} diff --git a/examples/starwars/tests/test_query.py b/examples/starwars/tests/test_query.py index ac57c664..e6a70735 100644 --- a/examples/starwars/tests/test_query.py +++ b/examples/starwars/tests/test_query.py @@ -1,11 +1,12 @@ - +from graphene.test import Client from ..data import setup from ..schema import schema setup() +client = Client(schema) -def test_hero_name_query(): +def test_hero_name_query(snapshot): query = ''' query HeroNameQuery { hero { @@ -13,17 +14,11 @@ def test_hero_name_query(): } } ''' - expected = { - 'hero': { - 'name': 'R2-D2' - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) + -def test_hero_name_and_friends_query(): +def test_hero_name_and_friends_query(snapshot): query = ''' query HeroNameAndFriendsQuery { hero { @@ -35,23 +30,10 @@ def test_hero_name_and_friends_query(): } } ''' - expected = { - 'hero': { - 'id': '2001', - 'name': 'R2-D2', - 'friends': [ - {'name': 'Luke Skywalker'}, - {'name': 'Han Solo'}, - {'name': 'Leia Organa'}, - ] - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_nested_query(): +def test_nested_query(snapshot): query = ''' query NestedQuery { hero { @@ -66,70 +48,10 @@ def test_nested_query(): } } ''' - expected = { - 'hero': { - 'name': 'R2-D2', - 'friends': [ - { - 'name': 'Luke Skywalker', - 'appearsIn': ['NEWHOPE', 'EMPIRE', 'JEDI'], - 'friends': [ - { - 'name': 'Han Solo', - }, - { - 'name': 'Leia Organa', - }, - { - 'name': 'C-3PO', - }, - { - 'name': 'R2-D2', - }, - ] - }, - { - 'name': 'Han Solo', - 'appearsIn': ['NEWHOPE', 'EMPIRE', 'JEDI'], - 'friends': [ - { - 'name': 'Luke Skywalker', - }, - { - 'name': 'Leia Organa', - }, - { - 'name': 'R2-D2', - }, - ] - }, - { - 'name': 'Leia Organa', - 'appearsIn': ['NEWHOPE', 'EMPIRE', 'JEDI'], - 'friends': [ - { - 'name': 'Luke Skywalker', - }, - { - 'name': 'Han Solo', - }, - { - 'name': 'C-3PO', - }, - { - 'name': 'R2-D2', - }, - ] - }, - ] - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_fetch_luke_query(): +def test_fetch_luke_query(snapshot): query = ''' query FetchLukeQuery { human(id: "1000") { @@ -137,17 +59,10 @@ def test_fetch_luke_query(): } } ''' - expected = { - 'human': { - 'name': 'Luke Skywalker', - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_fetch_some_id_query(): +def test_fetch_some_id_query(snapshot): query = ''' query FetchSomeIDQuery($someId: String!) { human(id: $someId) { @@ -158,17 +73,10 @@ def test_fetch_some_id_query(): params = { 'someId': '1000', } - expected = { - 'human': { - 'name': 'Luke Skywalker', - } - } - result = schema.execute(query, None, variable_values=params) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query, variable_values=params)) -def test_fetch_some_id_query2(): +def test_fetch_some_id_query2(snapshot): query = ''' query FetchSomeIDQuery($someId: String!) { human(id: $someId) { @@ -179,17 +87,10 @@ def test_fetch_some_id_query2(): params = { 'someId': '1002', } - expected = { - 'human': { - 'name': 'Han Solo', - } - } - result = schema.execute(query, None, variable_values=params) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query, variable_values=params)) -def test_invalid_id_query(): +def test_invalid_id_query(snapshot): query = ''' query humanQuery($id: String!) { human(id: $id) { @@ -200,15 +101,10 @@ def test_invalid_id_query(): params = { 'id': 'not a valid id', } - expected = { - 'human': None - } - result = schema.execute(query, None, variable_values=params) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query, variable_values=params)) -def test_fetch_luke_aliased(): +def test_fetch_luke_aliased(snapshot): query = ''' query FetchLukeAliased { luke: human(id: "1000") { @@ -216,17 +112,10 @@ def test_fetch_luke_aliased(): } } ''' - expected = { - 'luke': { - 'name': 'Luke Skywalker', - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_fetch_luke_and_leia_aliased(): +def test_fetch_luke_and_leia_aliased(snapshot): query = ''' query FetchLukeAndLeiaAliased { luke: human(id: "1000") { @@ -237,20 +126,10 @@ def test_fetch_luke_and_leia_aliased(): } } ''' - expected = { - 'luke': { - 'name': 'Luke Skywalker', - }, - 'leia': { - 'name': 'Leia Organa', - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_duplicate_fields(): +def test_duplicate_fields(snapshot): query = ''' query DuplicateFields { luke: human(id: "1000") { @@ -263,22 +142,10 @@ def test_duplicate_fields(): } } ''' - expected = { - 'luke': { - 'name': 'Luke Skywalker', - 'homePlanet': 'Tatooine', - }, - 'leia': { - 'name': 'Leia Organa', - 'homePlanet': 'Alderaan', - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_use_fragment(): +def test_use_fragment(snapshot): query = ''' query UseFragment { luke: human(id: "1000") { @@ -293,22 +160,10 @@ def test_use_fragment(): homePlanet } ''' - expected = { - 'luke': { - 'name': 'Luke Skywalker', - 'homePlanet': 'Tatooine', - }, - 'leia': { - 'name': 'Leia Organa', - 'homePlanet': 'Alderaan', - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_check_type_of_r2(): +def test_check_type_of_r2(snapshot): query = ''' query CheckTypeOfR2 { hero { @@ -317,18 +172,10 @@ def test_check_type_of_r2(): } } ''' - expected = { - 'hero': { - '__typename': 'Droid', - 'name': 'R2-D2', - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_check_type_of_luke(): +def test_check_type_of_luke(snapshot): query = ''' query CheckTypeOfLuke { hero(episode: EMPIRE) { @@ -337,12 +184,4 @@ def test_check_type_of_luke(): } } ''' - expected = { - 'hero': { - '__typename': 'Human', - 'name': 'Luke Skywalker', - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) diff --git a/examples/starwars_relay/tests/snapshots/__init__.py b/examples/starwars_relay/tests/snapshots/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/starwars_relay/tests/snapshots/snap_test_connections.py b/examples/starwars_relay/tests/snapshots/snap_test_connections.py new file mode 100644 index 00000000..b0efcc97 --- /dev/null +++ b/examples/starwars_relay/tests/snapshots/snap_test_connections.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# snapshottest: v1 +# https://pypi.python.org/pypi/snapshottest + +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_correct_fetch_first_ship_rebels 1'] = { + 'data': { + 'rebels': { + 'name': 'Alliance to Restore the Republic', + 'ships': { + 'pageInfo': { + 'startCursor': 'YXJyYXljb25uZWN0aW9uOjA=', + 'endCursor': 'YXJyYXljb25uZWN0aW9uOjA=', + 'hasNextPage': True, + 'hasPreviousPage': False + }, + 'edges': [ + { + 'cursor': 'YXJyYXljb25uZWN0aW9uOjA=', + 'node': { + 'name': 'X-Wing' + } + } + ] + } + } + } +} diff --git a/examples/starwars_relay/tests/snapshots/snap_test_mutation.py b/examples/starwars_relay/tests/snapshots/snap_test_mutation.py new file mode 100644 index 00000000..95f414cc --- /dev/null +++ b/examples/starwars_relay/tests/snapshots/snap_test_mutation.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +# snapshottest: v1 +# https://pypi.python.org/pypi/snapshottest + +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_mutations 1'] = { + 'data': { + 'introduceShip': { + 'ship': { + 'id': 'U2hpcDo5', + 'name': 'Peter' + }, + 'faction': { + 'name': 'Alliance to Restore the Republic', + 'ships': { + 'edges': [ + { + 'node': { + 'id': 'U2hpcDox', + 'name': 'X-Wing' + } + }, + { + 'node': { + 'id': 'U2hpcDoy', + 'name': 'Y-Wing' + } + }, + { + 'node': { + 'id': 'U2hpcDoz', + 'name': 'A-Wing' + } + }, + { + 'node': { + 'id': 'U2hpcDo0', + 'name': 'Millenium Falcon' + } + }, + { + 'node': { + 'id': 'U2hpcDo1', + 'name': 'Home One' + } + }, + { + 'node': { + 'id': 'U2hpcDo5', + 'name': 'Peter' + } + } + ] + } + } + } + } +} diff --git a/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py b/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py new file mode 100644 index 00000000..07e20039 --- /dev/null +++ b/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +# snapshottest: v1 +# https://pypi.python.org/pypi/snapshottest + +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_correctly_fetches_id_name_rebels 1'] = { + 'data': { + 'rebels': { + 'id': 'RmFjdGlvbjox', + 'name': 'Alliance to Restore the Republic' + } + } +} + +snapshots['test_correctly_refetches_rebels 1'] = { + 'data': { + 'node': { + 'id': 'RmFjdGlvbjox', + 'name': 'Alliance to Restore the Republic' + } + } +} + +snapshots['test_correctly_fetches_id_name_empire 1'] = { + 'data': { + 'empire': { + 'id': 'RmFjdGlvbjoy', + 'name': 'Galactic Empire' + } + } +} + +snapshots['test_correctly_refetches_empire 1'] = { + 'data': { + 'node': { + 'id': 'RmFjdGlvbjoy', + 'name': 'Galactic Empire' + } + } +} + +snapshots['test_correctly_refetches_xwing 1'] = { + 'data': { + 'node': { + 'id': 'U2hpcDox', + 'name': 'X-Wing' + } + } +} diff --git a/examples/starwars_relay/tests/test_connections.py b/examples/starwars_relay/tests/test_connections.py index d77122b9..e3ecfa7b 100644 --- a/examples/starwars_relay/tests/test_connections.py +++ b/examples/starwars_relay/tests/test_connections.py @@ -1,10 +1,13 @@ +from graphene.test import Client from ..data import setup from ..schema import schema setup() +client = Client(schema) -def test_correct_fetch_first_ship_rebels(): + +def test_correct_fetch_first_ship_rebels(snapshot): query = ''' query RebelsShipsQuery { rebels { @@ -26,27 +29,4 @@ def test_correct_fetch_first_ship_rebels(): } } ''' - expected = { - 'rebels': { - 'name': 'Alliance to Restore the Republic', - 'ships': { - 'pageInfo': { - 'startCursor': 'YXJyYXljb25uZWN0aW9uOjA=', - 'endCursor': 'YXJyYXljb25uZWN0aW9uOjA=', - 'hasNextPage': True, - 'hasPreviousPage': False - }, - 'edges': [ - { - 'cursor': 'YXJyYXljb25uZWN0aW9uOjA=', - 'node': { - 'name': 'X-Wing' - } - } - ] - } - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) diff --git a/examples/starwars_relay/tests/test_mutation.py b/examples/starwars_relay/tests/test_mutation.py index e5ae2716..2c07f08c 100644 --- a/examples/starwars_relay/tests/test_mutation.py +++ b/examples/starwars_relay/tests/test_mutation.py @@ -1,10 +1,13 @@ +from graphene.test import Client from ..data import setup from ..schema import schema setup() +client = Client(schema) -def test_mutations(): + +def test_mutations(snapshot): query = ''' mutation MyMutation { introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) { @@ -26,51 +29,4 @@ def test_mutations(): } } ''' - expected = { - 'introduceShip': { - 'ship': { - 'id': 'U2hpcDo5', - 'name': 'Peter' - }, - 'faction': { - 'name': 'Alliance to Restore the Republic', - 'ships': { - 'edges': [{ - 'node': { - 'id': 'U2hpcDox', - 'name': 'X-Wing' - } - }, { - 'node': { - 'id': 'U2hpcDoy', - 'name': 'Y-Wing' - } - }, { - 'node': { - 'id': 'U2hpcDoz', - 'name': 'A-Wing' - } - }, { - 'node': { - 'id': 'U2hpcDo0', - 'name': 'Millenium Falcon' - } - }, { - 'node': { - 'id': 'U2hpcDo1', - 'name': 'Home One' - } - }, { - 'node': { - 'id': 'U2hpcDo5', - 'name': 'Peter' - } - }] - }, - } - } - } - result = schema.execute(query) - # raise result.errors[0].original_error, None, result.errors[0].stack - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) diff --git a/examples/starwars_relay/tests/test_objectidentification.py b/examples/starwars_relay/tests/test_objectidentification.py index 327d5b0c..d1b4c529 100644 --- a/examples/starwars_relay/tests/test_objectidentification.py +++ b/examples/starwars_relay/tests/test_objectidentification.py @@ -1,8 +1,11 @@ +from graphene.test import Client from ..data import setup from ..schema import schema setup() +client = Client(schema) + def test_str_schema(): assert str(schema) == '''schema { @@ -66,7 +69,7 @@ type ShipEdge { ''' -def test_correctly_fetches_id_name_rebels(): +def test_correctly_fetches_id_name_rebels(snapshot): query = ''' query RebelsQuery { rebels { @@ -75,18 +78,10 @@ def test_correctly_fetches_id_name_rebels(): } } ''' - expected = { - 'rebels': { - 'id': 'RmFjdGlvbjox', - 'name': 'Alliance to Restore the Republic' - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_correctly_refetches_rebels(): +def test_correctly_refetches_rebels(snapshot): query = ''' query RebelsRefetchQuery { node(id: "RmFjdGlvbjox") { @@ -97,18 +92,10 @@ def test_correctly_refetches_rebels(): } } ''' - expected = { - 'node': { - 'id': 'RmFjdGlvbjox', - 'name': 'Alliance to Restore the Republic' - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_correctly_fetches_id_name_empire(): +def test_correctly_fetches_id_name_empire(snapshot): query = ''' query EmpireQuery { empire { @@ -117,18 +104,10 @@ def test_correctly_fetches_id_name_empire(): } } ''' - expected = { - 'empire': { - 'id': 'RmFjdGlvbjoy', - 'name': 'Galactic Empire' - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_correctly_refetches_empire(): +def test_correctly_refetches_empire(snapshot): query = ''' query EmpireRefetchQuery { node(id: "RmFjdGlvbjoy") { @@ -139,18 +118,10 @@ def test_correctly_refetches_empire(): } } ''' - expected = { - 'node': { - 'id': 'RmFjdGlvbjoy', - 'name': 'Galactic Empire' - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) -def test_correctly_refetches_xwing(): +def test_correctly_refetches_xwing(snapshot): query = ''' query XWingRefetchQuery { node(id: "U2hpcDox") { @@ -161,12 +132,4 @@ def test_correctly_refetches_xwing(): } } ''' - expected = { - 'node': { - 'id': 'U2hpcDox', - 'name': 'X-Wing' - } - } - result = schema.execute(query) - assert not result.errors - assert result.data == expected + snapshot.assert_match(client.execute(query)) diff --git a/graphene/test/__init__.py b/graphene/test/__init__.py new file mode 100644 index 00000000..fccc80ce --- /dev/null +++ b/graphene/test/__init__.py @@ -0,0 +1,28 @@ +from graphene.types.schema import Schema + +from graphql.error import format_error + + +def format_execution_result(execution_result): + if execution_result: + response = {} + + if execution_result.errors: + response['errors'] = [format_error(e) for e in execution_result.errors] + + if not execution_result.invalid: + response['data'] = execution_result.data + + return response + + +class Client(object): + def __init__(self, schema, **execute_options): + assert isinstance(schema, Schema) + self.schema = schema + self.execute_options = execute_options + + def execute(self, *args, **kwargs): + return format_execution_result( + self.schema.execute(*args, **dict(self.execute_options, **kwargs)) + ) diff --git a/setup.py b/setup.py index 20742eef..215a97df 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ tests_require = [ 'pytest>=2.7.2', 'pytest-benchmark', 'pytest-cov', + 'snapshottest', 'coveralls', 'six', 'mock', From 60e29028a8c7e025d5bbfe1d9f730c937829c98c Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 10 Apr 2017 00:47:20 -0700 Subject: [PATCH 2/6] Added testing docs --- docs/index.rst | 1 + docs/testing/index.rst | 90 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 docs/testing/index.rst diff --git a/docs/index.rst b/docs/index.rst index 675051b3..f1761937 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ Contents: types/index execution/index relay/index + testing/index Integrations ----- diff --git a/docs/testing/index.rst b/docs/testing/index.rst new file mode 100644 index 00000000..0dcb0034 --- /dev/null +++ b/docs/testing/index.rst @@ -0,0 +1,90 @@ +=================== +Testing in Graphene +=================== + + +Automated testing is an extremely useful bug-killing tool for the modern developer. You can use a collection of tests – a test suite – to solve, or avoid, a number of problems: + +- When you’re writing new code, you can use tests to validate your code works as expected. +- When you’re refactoring or modifying old code, you can use tests to ensure your changes haven’t affected your application’s behavior unexpectedly. + +Testing a GraphQL application is a complex task, because a GraphQL application is made of several layers of logic – schema definition, schema validation, permissions and field resolution. + +With Graphene test-execution framework and assorted utilities, you can simulate GraphQL requests, execute mutations, inspect your application’s output and generally verify your code is doing what it should be doing. + + +Testing tools +------------- + +Graphene provides a small set of tools that come in handy when writing tests. + + +Test Client +~~~~~~~~~~~ + +The test client is a Python class that acts as a dummy GraphQL client, allowing you to test your views and interact with your Graphene-powered application programmatically. + +Some of the things you can do with the test client are: + +- Simulate Queries and Mutations and observe the response. +- Test that a given query request is rendered by a given Django template, with a template context that contains certain values. + + +Overview and a quick example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the test client, instantiate ``graphene.test.Client`` and retrieve GraphQL responses: + + +.. code:: python + + from graphene.test import Client + + def test_hey(): + client = Client(my_schema) + executed = client.execute('''{ hey }''') + assert executed == { + 'data': { + 'hey': 'hello!' + } + } + + +Snapshot testing +~~~~~~~~~~~~~~~~ + +As our APIs evolve, we need to know when our changes introduce any breaking changes that might break +some of the clients of our GraphQL app. + +However, writing tests and replicate the same response we expect from our GraphQL application can be +tedious and repetitive task, and sometimes it's easier to skip this process. + +Because of that, we recommend the usage of `SnapshotTest `_. + +SnapshotTest let us write all this tests in a breeze, as creates automatically the ``snapshots`` for us +the first time the test is executed. + + +Here is a simple example on how our tests will look if we use ``pytest``: + +.. code:: python + + def test_hey(snapshot): + client = Client(my_schema) + # This will create a snapshot dir and a snapshot file + # the first time the test is executed, with the response + # of the execution. + snapshot.assert_match(client.execute('''{ hey }''')) + + +If we are using ``unittest``: + +.. code:: python + + from snapshottest import TestCase + + class APITestCase(TestCase): + def test_api_me(self): + """Testing the API for /me""" + client = Client(my_schema) + self.assertMatchSnapshot(client.execute('''{ hey }''')) From b369ccfa0828817f41b647938d926f5c2a4cb192 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 10 Apr 2017 04:26:54 -0700 Subject: [PATCH 3/6] Updated snapshot files --- examples/starwars/tests/snapshots/snap_test_query.py | 5 +---- .../starwars_relay/tests/snapshots/snap_test_connections.py | 5 +---- .../starwars_relay/tests/snapshots/snap_test_mutation.py | 5 +---- .../tests/snapshots/snap_test_objectidentification.py | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/examples/starwars/tests/snapshots/snap_test_query.py b/examples/starwars/tests/snapshots/snap_test_query.py index 63c097d4..9314420b 100644 --- a/examples/starwars/tests/snapshots/snap_test_query.py +++ b/examples/starwars/tests/snapshots/snap_test_query.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- - -# snapshottest: v1 -# https://pypi.python.org/pypi/snapshottest - +# snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot diff --git a/examples/starwars_relay/tests/snapshots/snap_test_connections.py b/examples/starwars_relay/tests/snapshots/snap_test_connections.py index b0efcc97..cbf14d95 100644 --- a/examples/starwars_relay/tests/snapshots/snap_test_connections.py +++ b/examples/starwars_relay/tests/snapshots/snap_test_connections.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- - -# snapshottest: v1 -# https://pypi.python.org/pypi/snapshottest - +# snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot diff --git a/examples/starwars_relay/tests/snapshots/snap_test_mutation.py b/examples/starwars_relay/tests/snapshots/snap_test_mutation.py index 95f414cc..27c3f9bb 100644 --- a/examples/starwars_relay/tests/snapshots/snap_test_mutation.py +++ b/examples/starwars_relay/tests/snapshots/snap_test_mutation.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- - -# snapshottest: v1 -# https://pypi.python.org/pypi/snapshottest - +# snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot diff --git a/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py b/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py index 07e20039..a6095bb0 100644 --- a/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py +++ b/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- - -# snapshottest: v1 -# https://pypi.python.org/pypi/snapshottest - +# snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot From 917dc16ea64b3d896e81f5648dea0aebb78b468e Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 10 Apr 2017 16:10:35 -0700 Subject: [PATCH 4/6] Fixed format_error --- graphene/test/__init__.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/graphene/test/__init__.py b/graphene/test/__init__.py index fccc80ce..6c47fdeb 100644 --- a/graphene/test/__init__.py +++ b/graphene/test/__init__.py @@ -1,9 +1,17 @@ from graphene.types.schema import Schema -from graphql.error import format_error +from graphql.error import format_error as format_graphql_error +from graphql.error import GraphQLError -def format_execution_result(execution_result): +def default_format_error(error): + if isinstance(error, GraphQLError): + return format_graphql_error(error) + + return {'message': six.text_type(error)} + + +def format_execution_result(execution_result, format_error): if execution_result: response = {} @@ -17,12 +25,14 @@ def format_execution_result(execution_result): class Client(object): - def __init__(self, schema, **execute_options): + def __init__(self, schema, format_error=None, **execute_options): assert isinstance(schema, Schema) self.schema = schema self.execute_options = execute_options + self.format_error = format_error or default_format_error def execute(self, *args, **kwargs): return format_execution_result( - self.schema.execute(*args, **dict(self.execute_options, **kwargs)) + self.schema.execute(*args, **dict(self.execute_options, **kwargs)), + self.format_error ) From 038f81e8e1931e1f5fb55cad80773bf6c2d2d09c Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 10 Apr 2017 16:18:45 -0700 Subject: [PATCH 5/6] Added extra parameters section in testing docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @BossGrand 😉 --- docs/testing/index.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/testing/index.rst b/docs/testing/index.rst index 0dcb0034..516f0e8d 100644 --- a/docs/testing/index.rst +++ b/docs/testing/index.rst @@ -50,6 +50,27 @@ To use the test client, instantiate ``graphene.test.Client`` and retrieve GraphQ } +Execute parameters +~~~~~~~~~~~~~~~~~~ + +You can also add extra keyword arguments to the ``execute`` method, such as +``context_value``, ``root_value``, ``variable_values``, ...: + + +.. code:: python + + from graphene.test import Client + + def test_hey(): + client = Client(my_schema) + executed = client.execute('''{ hey }''', context_value={'user': 'Peter'}) + assert executed == { + 'data': { + 'hey': 'hello Peter!' + } + } + + Snapshot testing ~~~~~~~~~~~~~~~~ From 6c040e68a2474ec17f1d2ccd5a4b295ffab63dbd Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 10 Apr 2017 16:18:52 -0700 Subject: [PATCH 6/6] Fixed six import --- graphene/test/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/graphene/test/__init__.py b/graphene/test/__init__.py index 6c47fdeb..98c8547c 100644 --- a/graphene/test/__init__.py +++ b/graphene/test/__init__.py @@ -1,8 +1,9 @@ -from graphene.types.schema import Schema - +import six from graphql.error import format_error as format_graphql_error from graphql.error import GraphQLError +from graphene.types.schema import Schema + def default_format_error(error): if isinstance(error, GraphQLError):