mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-08 23:50:38 +03:00
Improved overall testing coverage
This commit is contained in:
parent
61d5556beb
commit
6eb1a48cb2
|
@ -66,6 +66,10 @@ class DjangoModelField(Field):
|
||||||
resolved = super(DjangoModelField, self).resolve(instance, args, info)
|
resolved = super(DjangoModelField, self).resolve(instance, args, info)
|
||||||
schema = info.schema.graphene_schema
|
schema = info.schema.graphene_schema
|
||||||
_type = self.get_object_type(schema)
|
_type = self.get_object_type(schema)
|
||||||
|
assert _type, ("Field %s cannot be retrieved as the "
|
||||||
|
"ObjectType is not registered by the schema" % (
|
||||||
|
self.field_name
|
||||||
|
))
|
||||||
return _type(resolved)
|
return _type(resolved)
|
||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
|
|
|
@ -121,7 +121,7 @@ class Field(object):
|
||||||
"""
|
"""
|
||||||
Displays the module, class and name of the field.
|
Displays the module, class and name of the field.
|
||||||
"""
|
"""
|
||||||
path = '%s[%s]' % (self.__class__.__name__, str(self.field_type))
|
path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
|
||||||
name = getattr(self, 'field_name', None)
|
name = getattr(self, 'field_name', None)
|
||||||
if name is not None:
|
if name is not None:
|
||||||
return '<%s: %s>' % (path, name)
|
return '<%s: %s>' % (path, name)
|
||||||
|
|
|
@ -19,6 +19,9 @@ class ObjectTypeMeta(type):
|
||||||
def is_interface(cls, parents):
|
def is_interface(cls, parents):
|
||||||
return Interface in parents
|
return Interface in parents
|
||||||
|
|
||||||
|
def is_mutation(cls, parents):
|
||||||
|
return Mutation in parents
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
super_new = super(ObjectTypeMeta, cls).__new__
|
super_new = super(ObjectTypeMeta, cls).__new__
|
||||||
parents = [b for b in bases if isinstance(b, cls)]
|
parents = [b for b in bases if isinstance(b, cls)]
|
||||||
|
@ -44,6 +47,10 @@ class ObjectTypeMeta(type):
|
||||||
new_class.add_to_class('_meta', new_class.options_cls(meta))
|
new_class.add_to_class('_meta', new_class.options_cls(meta))
|
||||||
|
|
||||||
new_class._meta.interface = new_class.is_interface(parents)
|
new_class._meta.interface = new_class.is_interface(parents)
|
||||||
|
new_class._meta.mutation = new_class.is_mutation(parents)
|
||||||
|
|
||||||
|
assert not (new_class._meta.interface and new_class._meta.mutation)
|
||||||
|
|
||||||
# Add all attributes to the class.
|
# Add all attributes to the class.
|
||||||
for obj_name, obj in attrs.items():
|
for obj_name, obj in attrs.items():
|
||||||
new_class.add_to_class(obj_name, obj)
|
new_class.add_to_class(obj_name, obj)
|
||||||
|
@ -155,5 +162,9 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Mutation(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -15,9 +15,9 @@ def get_node_type(schema, obj):
|
||||||
return obj.internal_type(schema)
|
return obj.internal_type(schema)
|
||||||
|
|
||||||
|
|
||||||
def get_node(schema, globalId, *args):
|
def get_node(schema, global_id, *args):
|
||||||
resolvedGlobalId = from_global_id(globalId)
|
resolved_global_id = from_global_id(global_id)
|
||||||
_type, _id = resolvedGlobalId.type, resolvedGlobalId.id
|
_type, _id = resolved_global_id.type, resolved_global_id.id
|
||||||
object_type = schema.get_type(_type)
|
object_type = schema.get_type(_type)
|
||||||
if not object_type or not issubclass(object_type, BaseNode):
|
if not object_type or not issubclass(object_type, BaseNode):
|
||||||
raise Exception("The type %s is not a Node" % _type)
|
raise Exception("The type %s is not a Node" % _type)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.conf.urls import url
|
||||||
|
|
||||||
from graphene.contrib.django.views import GraphQLView
|
from graphene.contrib.django.views import GraphQLView
|
||||||
|
|
||||||
|
import graphene
|
||||||
from graphene import Schema
|
from graphene import Schema
|
||||||
from graphene.contrib.django.types import (
|
from graphene.contrib.django.types import (
|
||||||
DjangoNode,
|
DjangoNode,
|
||||||
|
@ -20,9 +21,14 @@ class Character(DjangoNode):
|
||||||
|
|
||||||
|
|
||||||
class Human(DjangoNode):
|
class Human(DjangoNode):
|
||||||
|
raises = graphene.StringField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Article
|
model = Article
|
||||||
|
|
||||||
|
def resolve_raises(self, *args):
|
||||||
|
raise Exception("This field should raise exception")
|
||||||
|
|
||||||
def get_node(self, id):
|
def get_node(self, id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,14 @@ def test_client_get_good_query(settings, client):
|
||||||
assert json_response == expected_json
|
assert json_response == expected_json
|
||||||
|
|
||||||
|
|
||||||
|
def test_client_get_good_query_with_raise(settings, client):
|
||||||
|
settings.ROOT_URLCONF = 'tests.contrib_django.test_urls'
|
||||||
|
response = client.get('/graphql', {'query': '{ raises }'})
|
||||||
|
json_response = format_response(response)
|
||||||
|
assert json_response['errors'][0]['message'] == 'This field should raise exception'
|
||||||
|
assert json_response['data']['raises'] is None
|
||||||
|
|
||||||
|
|
||||||
def test_client_post_good_query(settings, client):
|
def test_client_post_good_query(settings, client):
|
||||||
settings.ROOT_URLCONF = 'tests.contrib_django.test_urls'
|
settings.ROOT_URLCONF = 'tests.contrib_django.test_urls'
|
||||||
response = client.post('/graphql', json.dumps({'query': '{ headline }'}), 'application/json')
|
response = client.post('/graphql', json.dumps({'query': '{ headline }'}), 'application/json')
|
||||||
|
|
|
@ -4,6 +4,7 @@ from pytest import raises
|
||||||
from graphene.core.fields import (
|
from graphene.core.fields import (
|
||||||
Field,
|
Field,
|
||||||
StringField,
|
StringField,
|
||||||
|
NonNullField
|
||||||
)
|
)
|
||||||
|
|
||||||
from graphene.core.options import Options
|
from graphene.core.options import Options
|
||||||
|
@ -27,6 +28,9 @@ class ObjectType(object):
|
||||||
def can_resolve(self, *args):
|
def can_resolve(self, *args):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "ObjectType"
|
||||||
|
|
||||||
ot = ObjectType()
|
ot = ObjectType()
|
||||||
|
|
||||||
ObjectType._meta.contribute_to_class(ObjectType, '_meta')
|
ObjectType._meta.contribute_to_class(ObjectType, '_meta')
|
||||||
|
@ -69,6 +73,12 @@ def test_stringfield_type():
|
||||||
assert f.internal_type(schema) == GraphQLString
|
assert f.internal_type(schema) == GraphQLString
|
||||||
|
|
||||||
|
|
||||||
|
def test_nonnullfield_type():
|
||||||
|
f = NonNullField(StringField())
|
||||||
|
f.contribute_to_class(ot, 'field_name')
|
||||||
|
assert isinstance(f.internal_type(schema), GraphQLNonNull)
|
||||||
|
|
||||||
|
|
||||||
def test_stringfield_type_required():
|
def test_stringfield_type_required():
|
||||||
f = StringField(required=True)
|
f = StringField(required=True)
|
||||||
f.contribute_to_class(ot, 'field_name')
|
f.contribute_to_class(ot, 'field_name')
|
||||||
|
@ -108,3 +118,61 @@ def test_field_resolve_type_custom():
|
||||||
f.contribute_to_class(ot, 'field_name')
|
f.contribute_to_class(ot, 'field_name')
|
||||||
field_type = f.get_object_type(s)
|
field_type = f.get_object_type(s)
|
||||||
assert field_type == ot
|
assert field_type == ot
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_orders():
|
||||||
|
f1 = Field(None)
|
||||||
|
f2 = Field(None)
|
||||||
|
assert f1 < f2
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_orders_wrong_type():
|
||||||
|
field = Field(None)
|
||||||
|
try:
|
||||||
|
assert not field < 1
|
||||||
|
except TypeError:
|
||||||
|
# Fix exception raising in Python3+
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_eq():
|
||||||
|
f1 = Field(None)
|
||||||
|
f2 = Field(None)
|
||||||
|
assert f1 != f2
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_eq_wrong_type():
|
||||||
|
field = Field(None)
|
||||||
|
assert field != 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_hash():
|
||||||
|
f1 = Field(None)
|
||||||
|
f2 = Field(None)
|
||||||
|
assert hash(f1) != hash(f2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_none_type_raises_error():
|
||||||
|
s = Schema()
|
||||||
|
f = Field(None)
|
||||||
|
f.contribute_to_class(ot, 'field_name')
|
||||||
|
with raises(Exception) as excinfo:
|
||||||
|
f.internal_field(s)
|
||||||
|
assert str(excinfo.value) == "Internal type for field ObjectType.field_name is None"
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_str():
|
||||||
|
f = StringField()
|
||||||
|
f.contribute_to_class(ot, 'field_name')
|
||||||
|
assert str(f) == "ObjectType.field_name"
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_repr():
|
||||||
|
f = StringField()
|
||||||
|
assert repr(f) == "<graphene.core.fields.StringField>"
|
||||||
|
|
||||||
|
|
||||||
|
def test_field_repr_contributed():
|
||||||
|
f = StringField()
|
||||||
|
f.contribute_to_class(ot, 'field_name')
|
||||||
|
assert repr(f) == "<graphene.core.fields.StringField: field_name>"
|
||||||
|
|
18
tests/core/test_scalars.py
Normal file
18
tests/core/test_scalars.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from graphene.core.scalars import (
|
||||||
|
GraphQLSkipField
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_skipfield_serialize():
|
||||||
|
f = GraphQLSkipField
|
||||||
|
assert f.serialize('a') is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_skipfield_parse_value():
|
||||||
|
f = GraphQLSkipField
|
||||||
|
assert f.parse_value('a') is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_skipfield_parse_literal():
|
||||||
|
f = GraphQLSkipField
|
||||||
|
assert f.parse_literal('a') is None
|
|
@ -109,3 +109,33 @@ def test_schema_get_type_map():
|
||||||
schema.schema.get_type_map().keys(),
|
schema.schema.get_type_map().keys(),
|
||||||
['__Field', 'String', 'Pet', 'Character', '__InputValue', '__Directive', '__TypeKind', '__Schema', '__Type', 'Human', '__EnumValue', 'Boolean']
|
['__Field', 'String', 'Pet', 'Character', '__InputValue', '__Directive', '__TypeKind', '__Schema', '__Type', 'Human', '__EnumValue', 'Boolean']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema_no_query():
|
||||||
|
schema = Schema(name='My own schema')
|
||||||
|
with raises(Exception) as excinfo:
|
||||||
|
schema.schema
|
||||||
|
assert 'define a base query type' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema_register():
|
||||||
|
schema = Schema(name='My own schema')
|
||||||
|
|
||||||
|
@schema.register
|
||||||
|
class MyType(ObjectType):
|
||||||
|
type = StringField(resolve=lambda *_: 'Dog')
|
||||||
|
|
||||||
|
assert schema.get_type('MyType') == MyType
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema_introspect():
|
||||||
|
schema = Schema(name='My own schema')
|
||||||
|
|
||||||
|
class MyType(ObjectType):
|
||||||
|
type = StringField(resolve=lambda *_: 'Dog')
|
||||||
|
|
||||||
|
schema.query = MyType
|
||||||
|
|
||||||
|
introspection = schema.introspect()
|
||||||
|
assert '__schema' in introspection
|
||||||
|
|
||||||
|
|
43
tests/relay/test_relayfields.py
Normal file
43
tests/relay/test_relayfields.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
from pytest import raises
|
||||||
|
|
||||||
|
import graphene
|
||||||
|
from graphene import relay
|
||||||
|
|
||||||
|
schema = graphene.Schema()
|
||||||
|
|
||||||
|
|
||||||
|
class MyType(object):
|
||||||
|
name = 'my'
|
||||||
|
|
||||||
|
|
||||||
|
class MyNode(relay.Node):
|
||||||
|
name = graphene.StringField()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_node(cls, id):
|
||||||
|
return MyNode(MyType())
|
||||||
|
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
my_node = relay.NodeField(MyNode)
|
||||||
|
|
||||||
|
|
||||||
|
schema.query = Query
|
||||||
|
|
||||||
|
|
||||||
|
def test_nodefield_query():
|
||||||
|
query = '''
|
||||||
|
query RebelsShipsQuery {
|
||||||
|
myNode(id:"TXlOb2RlOjE=") {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
expected = {
|
||||||
|
'myNode': {
|
||||||
|
'name': 'my'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = schema.execute(query)
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == expected
|
|
@ -1,18 +1,28 @@
|
||||||
from collections import namedtuple
|
from .models import Ship, Faction, Character
|
||||||
|
|
||||||
from .models import Ship, Faction
|
|
||||||
|
|
||||||
|
|
||||||
def initialize():
|
def initialize():
|
||||||
|
human = Character(
|
||||||
|
name='Human'
|
||||||
|
)
|
||||||
|
human.save()
|
||||||
|
|
||||||
|
droid = Character(
|
||||||
|
name='Droid'
|
||||||
|
)
|
||||||
|
droid.save()
|
||||||
|
|
||||||
rebels = Faction(
|
rebels = Faction(
|
||||||
id='1',
|
id='1',
|
||||||
name='Alliance to Restore the Republic',
|
name='Alliance to Restore the Republic',
|
||||||
|
hero=human
|
||||||
)
|
)
|
||||||
rebels.save()
|
rebels.save()
|
||||||
|
|
||||||
empire = Faction(
|
empire = Faction(
|
||||||
id='2',
|
id='2',
|
||||||
name='Galactic Empire',
|
name='Galactic Empire',
|
||||||
|
hero=droid
|
||||||
)
|
)
|
||||||
empire.save()
|
empire.save()
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,16 @@ from __future__ import absolute_import
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Character(models.Model):
|
||||||
|
name = models.CharField(max_length=50)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class Faction(models.Model):
|
class Faction(models.Model):
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
|
hero = models.ForeignKey(Character)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -4,7 +4,8 @@ from graphene.contrib.django import (
|
||||||
DjangoObjectType,
|
DjangoObjectType,
|
||||||
DjangoNode
|
DjangoNode
|
||||||
)
|
)
|
||||||
from .models import Ship as ShipModel, Faction as FactionModel
|
from .models import (
|
||||||
|
Ship as ShipModel, Faction as FactionModel, Character as CharacterModel)
|
||||||
from .data import (
|
from .data import (
|
||||||
getFaction,
|
getFaction,
|
||||||
getShip,
|
getShip,
|
||||||
|
@ -17,7 +18,6 @@ schema = graphene.Schema(name='Starwars Django Relay Schema')
|
||||||
|
|
||||||
|
|
||||||
class Ship(DjangoNode):
|
class Ship(DjangoNode):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ShipModel
|
model = ShipModel
|
||||||
|
|
||||||
|
@ -26,8 +26,13 @@ class Ship(DjangoNode):
|
||||||
return Ship(getShip(id))
|
return Ship(getShip(id))
|
||||||
|
|
||||||
|
|
||||||
class Faction(DjangoNode):
|
@schema.register
|
||||||
|
class CharacterModel(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = CharacterModel
|
||||||
|
|
||||||
|
|
||||||
|
class Faction(DjangoNode):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FactionModel
|
model = FactionModel
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,9 @@ def test_correct_fetch_first_ship_rebels():
|
||||||
query RebelsShipsQuery {
|
query RebelsShipsQuery {
|
||||||
rebels {
|
rebels {
|
||||||
name,
|
name,
|
||||||
|
hero {
|
||||||
|
name
|
||||||
|
}
|
||||||
ships(first: 1) {
|
ships(first: 1) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -27,6 +30,9 @@ def test_correct_fetch_first_ship_rebels():
|
||||||
expected = {
|
expected = {
|
||||||
'rebels': {
|
'rebels': {
|
||||||
'name': 'Alliance to Restore the Republic',
|
'name': 'Alliance to Restore the Republic',
|
||||||
|
'hero': {
|
||||||
|
'name': 'Human'
|
||||||
|
},
|
||||||
'ships': {
|
'ships': {
|
||||||
'edges': [
|
'edges': [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user