mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-02 12:44:15 +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)
|
||||
schema = info.schema.graphene_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)
|
||||
|
||||
@memoize
|
||||
|
|
|
@ -121,7 +121,7 @@ class Field(object):
|
|||
"""
|
||||
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)
|
||||
if name is not None:
|
||||
return '<%s: %s>' % (path, name)
|
||||
|
|
|
@ -19,6 +19,9 @@ class ObjectTypeMeta(type):
|
|||
def is_interface(cls, parents):
|
||||
return Interface in parents
|
||||
|
||||
def is_mutation(cls, parents):
|
||||
return Mutation in parents
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
super_new = super(ObjectTypeMeta, cls).__new__
|
||||
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._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.
|
||||
for obj_name, obj in attrs.items():
|
||||
new_class.add_to_class(obj_name, obj)
|
||||
|
@ -155,5 +162,9 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
|||
pass
|
||||
|
||||
|
||||
class Mutation(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||
pass
|
||||
|
||||
|
||||
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
||||
pass
|
||||
|
|
|
@ -15,9 +15,9 @@ def get_node_type(schema, obj):
|
|||
return obj.internal_type(schema)
|
||||
|
||||
|
||||
def get_node(schema, globalId, *args):
|
||||
resolvedGlobalId = from_global_id(globalId)
|
||||
_type, _id = resolvedGlobalId.type, resolvedGlobalId.id
|
||||
def get_node(schema, global_id, *args):
|
||||
resolved_global_id = from_global_id(global_id)
|
||||
_type, _id = resolved_global_id.type, resolved_global_id.id
|
||||
object_type = schema.get_type(_type)
|
||||
if not object_type or not issubclass(object_type, BaseNode):
|
||||
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
|
||||
|
||||
import graphene
|
||||
from graphene import Schema
|
||||
from graphene.contrib.django.types import (
|
||||
DjangoNode,
|
||||
|
@ -20,9 +21,14 @@ class Character(DjangoNode):
|
|||
|
||||
|
||||
class Human(DjangoNode):
|
||||
raises = graphene.StringField()
|
||||
|
||||
class Meta:
|
||||
model = Article
|
||||
|
||||
def resolve_raises(self, *args):
|
||||
raise Exception("This field should raise exception")
|
||||
|
||||
def get_node(self, id):
|
||||
pass
|
||||
|
||||
|
|
|
@ -72,6 +72,14 @@ def test_client_get_good_query(settings, client):
|
|||
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):
|
||||
settings.ROOT_URLCONF = 'tests.contrib_django.test_urls'
|
||||
response = client.post('/graphql', json.dumps({'query': '{ headline }'}), 'application/json')
|
||||
|
|
|
@ -4,6 +4,7 @@ from pytest import raises
|
|||
from graphene.core.fields import (
|
||||
Field,
|
||||
StringField,
|
||||
NonNullField
|
||||
)
|
||||
|
||||
from graphene.core.options import Options
|
||||
|
@ -27,6 +28,9 @@ class ObjectType(object):
|
|||
def can_resolve(self, *args):
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
return "ObjectType"
|
||||
|
||||
ot = ObjectType()
|
||||
|
||||
ObjectType._meta.contribute_to_class(ObjectType, '_meta')
|
||||
|
@ -69,6 +73,12 @@ def test_stringfield_type():
|
|||
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():
|
||||
f = StringField(required=True)
|
||||
f.contribute_to_class(ot, 'field_name')
|
||||
|
@ -108,3 +118,61 @@ def test_field_resolve_type_custom():
|
|||
f.contribute_to_class(ot, 'field_name')
|
||||
field_type = f.get_object_type(s)
|
||||
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(),
|
||||
['__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
|
||||
from .models import Ship, Faction, Character
|
||||
|
||||
|
||||
def initialize():
|
||||
human = Character(
|
||||
name='Human'
|
||||
)
|
||||
human.save()
|
||||
|
||||
droid = Character(
|
||||
name='Droid'
|
||||
)
|
||||
droid.save()
|
||||
|
||||
rebels = Faction(
|
||||
id='1',
|
||||
name='Alliance to Restore the Republic',
|
||||
hero=human
|
||||
)
|
||||
rebels.save()
|
||||
|
||||
empire = Faction(
|
||||
id='2',
|
||||
name='Galactic Empire',
|
||||
hero=droid
|
||||
)
|
||||
empire.save()
|
||||
|
||||
|
|
|
@ -2,8 +2,16 @@ from __future__ import absolute_import
|
|||
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):
|
||||
name = models.CharField(max_length=50)
|
||||
hero = models.ForeignKey(Character)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -4,7 +4,8 @@ from graphene.contrib.django import (
|
|||
DjangoObjectType,
|
||||
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 (
|
||||
getFaction,
|
||||
getShip,
|
||||
|
@ -17,7 +18,6 @@ schema = graphene.Schema(name='Starwars Django Relay Schema')
|
|||
|
||||
|
||||
class Ship(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = ShipModel
|
||||
|
||||
|
@ -26,8 +26,13 @@ class Ship(DjangoNode):
|
|||
return Ship(getShip(id))
|
||||
|
||||
|
||||
class Faction(DjangoNode):
|
||||
@schema.register
|
||||
class CharacterModel(DjangoObjectType):
|
||||
class Meta:
|
||||
model = CharacterModel
|
||||
|
||||
|
||||
class Faction(DjangoNode):
|
||||
class Meta:
|
||||
model = FactionModel
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@ def test_correct_fetch_first_ship_rebels():
|
|||
query RebelsShipsQuery {
|
||||
rebels {
|
||||
name,
|
||||
hero {
|
||||
name
|
||||
}
|
||||
ships(first: 1) {
|
||||
edges {
|
||||
node {
|
||||
|
@ -27,6 +30,9 @@ def test_correct_fetch_first_ship_rebels():
|
|||
expected = {
|
||||
'rebels': {
|
||||
'name': 'Alliance to Restore the Republic',
|
||||
'hero': {
|
||||
'name': 'Human'
|
||||
},
|
||||
'ships': {
|
||||
'edges': [
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user