Django integration for Graphene enabling GraphQL APIs in Django applications
—
Test case classes and utility functions for GraphQL testing in Django applications with assertion helpers, query execution, and comprehensive testing patterns. Provides tools for unit testing GraphQL schemas, resolvers, and mutations.
Test case base class for GraphQL testing with built-in GraphQL execution and assertion helpers.
class GraphQLTestCase(django.test.TestCase):
"""
Test case base class for GraphQL testing.
Provides utility methods for executing GraphQL queries and mutations
in Django test environment with assertion helpers and error handling.
"""
GRAPHQL_SCHEMA = None # GraphQL schema for testing
GRAPHQL_URL = '/graphql/' # GraphQL endpoint URL
def query(self, query, op_name=None, input_data=None, variables=None, headers=None):
"""
Execute GraphQL query in test environment.
Parameters:
- query: GraphQL query string
- op_name: Operation name for named queries
- input_data: Input data for mutations (deprecated, use variables)
- variables: Query variables
- headers: HTTP headers
Returns:
dict: GraphQL response data
"""
def assertResponseNoErrors(self, resp, msg=None):
"""
Assert GraphQL response has no errors.
Parameters:
- resp: GraphQL response
- msg: Custom assertion message
Raises:
AssertionError: If response contains errors
"""
def assertResponseHasErrors(self, resp, msg=None):
"""
Assert GraphQL response has errors.
Parameters:
- resp: GraphQL response
- msg: Custom assertion message
Raises:
AssertionError: If response contains no errors
"""
def setUp(self):
"""Set up test case with schema configuration."""
def _client_query(self, query, op_name=None, input_data=None, variables=None, headers=None):
"""
Internal method for client query execution.
Parameters:
- query: GraphQL query string
- op_name: Operation name
- input_data: Input data
- variables: Query variables
- headers: HTTP headers
Returns:
django.http.HttpResponse: HTTP response
"""Helper functions for GraphQL query execution and testing patterns outside of test case classes.
def graphql_query(query, variables=None, headers=None, client=None, graphql_url='/graphql/'):
"""
Utility function for making GraphQL requests in tests.
Parameters:
- query: GraphQL query string
- variables: Query variables dict
- headers: HTTP headers dict
- client: Django test client instance
- graphql_url: GraphQL endpoint URL
Returns:
django.http.HttpResponse: HTTP response with GraphQL result
"""
def format_graphql_query(query, variables=None, operation_name=None):
"""
Format GraphQL query with variables and operation name.
Parameters:
- query: GraphQL query string
- variables: Query variables
- operation_name: GraphQL operation name
Returns:
dict: Formatted query data for HTTP request
"""
def extract_graphql_errors(response):
"""
Extract GraphQL errors from response.
Parameters:
- response: HTTP response with GraphQL data
Returns:
list: List of GraphQL error objects
"""from graphene_django.utils.testing import GraphQLTestCase
from myapp.schema import schema
class UserQueryTest(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def setUp(self):
self.user = User.objects.create(
username='testuser',
email='test@example.com'
)
def test_users_query(self):
query = '''
query {
users {
id
username
email
}
}
'''
response = self.query(query)
self.assertResponseNoErrors(response)
users = response['data']['users']
self.assertEqual(len(users), 1)
self.assertEqual(users[0]['username'], 'testuser')
def test_user_by_id_query(self):
query = '''
query GetUser($id: ID!) {
user(id: $id) {
username
email
}
}
'''
variables = {'id': str(self.user.id)}
response = self.query(query, variables=variables)
self.assertResponseNoErrors(response)
user_data = response['data']['user']
self.assertEqual(user_data['username'], 'testuser')
self.assertEqual(user_data['email'], 'test@example.com')class UserMutationTest(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def test_create_user_mutation(self):
mutation = '''
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
user {
id
username
email
}
errors {
field
messages
}
}
}
'''
variables = {
'input': {
'username': 'newuser',
'email': 'new@example.com',
'password': 'securepassword123'
}
}
response = self.query(mutation, variables=variables)
self.assertResponseNoErrors(response)
result = response['data']['createUser']
self.assertEqual(len(result['errors']), 0)
self.assertEqual(result['user']['username'], 'newuser')
# Verify user was created in database
user = User.objects.get(username='newuser')
self.assertEqual(user.email, 'new@example.com')
def test_create_user_validation_error(self):
# Create existing user
User.objects.create(username='existing', email='existing@example.com')
mutation = '''
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
user {
id
}
errors {
field
messages
}
}
}
'''
variables = {
'input': {
'username': 'existing', # This should cause validation error
'email': 'new@example.com'
}
}
response = self.query(mutation, variables=variables)
self.assertResponseNoErrors(response) # No GraphQL errors
result = response['data']['createUser']
self.assertIsNone(result['user'])
self.assertGreater(len(result['errors']), 0)
self.assertEqual(result['errors'][0]['field'], 'username')from django.contrib.auth import get_user_model
from django.test import override_settings
User = get_user_model()
class AuthenticatedGraphQLTest(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
def test_authenticated_query(self):
# Login user
self.client.force_login(self.user)
query = '''
query {
me {
id
username
email
}
}
'''
response = self.query(query)
self.assertResponseNoErrors(response)
me_data = response['data']['me']
self.assertEqual(me_data['username'], 'testuser')
def test_unauthenticated_query_error(self):
query = '''
query {
me {
id
username
}
}
'''
response = self.query(query)
self.assertResponseHasErrors(response)
errors = response['errors']
self.assertIn('authentication', errors[0]['message'].lower())
def test_permission_required_mutation(self):
# Create staff user
staff_user = User.objects.create_user(
username='staff',
email='staff@example.com',
password='staffpass',
is_staff=True
)
mutation = '''
mutation DeleteUser($id: ID!) {
deleteUser(id: $id) {
success
errors {
field
messages
}
}
}
'''
# Test without permission
self.client.force_login(self.user)
response = self.query(mutation, variables={'id': str(self.user.id)})
self.assertResponseHasErrors(response)
# Test with permission
self.client.force_login(staff_user)
response = self.query(mutation, variables={'id': str(self.user.id)})
self.assertResponseNoErrors(response)class PaginationTest(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def setUp(self):
# Create multiple users for pagination testing
for i in range(10):
User.objects.create(
username=f'user{i}',
email=f'user{i}@example.com'
)
def test_connection_pagination(self):
query = '''
query GetUsers($first: Int, $after: String) {
users(first: $first, after: $after) {
edges {
node {
id
username
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}
'''
# First page
response = self.query(query, variables={'first': 3})
self.assertResponseNoErrors(response)
connection = response['data']['users']
self.assertEqual(len(connection['edges']), 3)
self.assertTrue(connection['pageInfo']['hasNextPage'])
self.assertFalse(connection['pageInfo']['hasPreviousPage'])
# Second page
end_cursor = connection['pageInfo']['endCursor']
response = self.query(query, variables={'first': 3, 'after': end_cursor})
self.assertResponseNoErrors(response)
connection = response['data']['users']
self.assertEqual(len(connection['edges']), 3)
self.assertTrue(connection['pageInfo']['hasPreviousPage'])class FilteringTest(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def setUp(self):
self.active_user = User.objects.create(
username='active',
email='active@example.com',
is_active=True
)
self.inactive_user = User.objects.create(
username='inactive',
email='inactive@example.com',
is_active=False
)
def test_filter_by_active_status(self):
query = '''
query GetUsers($isActive: Boolean) {
users(isActive: $isActive) {
edges {
node {
username
isActive
}
}
}
}
'''
# Test active users
response = self.query(query, variables={'isActive': True})
self.assertResponseNoErrors(response)
users = response['data']['users']['edges']
self.assertEqual(len(users), 1)
self.assertEqual(users[0]['node']['username'], 'active')
# Test inactive users
response = self.query(query, variables={'isActive': False})
self.assertResponseNoErrors(response)
users = response['data']['users']['edges']
self.assertEqual(len(users), 1)
self.assertEqual(users[0]['node']['username'], 'inactive')class ErrorHandlingTest(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def test_invalid_query_syntax(self):
invalid_query = '''
query {
users {
id
nonexistentField
}
}
'''
response = self.query(invalid_query)
self.assertResponseHasErrors(response)
errors = response.get('errors', [])
self.assertGreater(len(errors), 0)
def test_resolver_exception(self):
query = '''
query {
userThatThrowsError {
id
}
}
'''
response = self.query(query)
self.assertResponseHasErrors(response)
errors = response.get('errors', [])
self.assertIn('error', errors[0]['message'].lower())
def test_custom_error_formatting(self):
# Override error formatting for sensitive information
with override_settings(DEBUG=False):
query = '''
query {
sensitiveOperation {
data
}
}
'''
response = self.query(query)
self.assertResponseHasErrors(response)
# In production, errors should be generic
errors = response.get('errors', [])
self.assertNotIn('database', errors[0]['message'].lower())class CustomContextTest(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def test_custom_headers(self):
query = '''
query {
requestInfo {
headers
userAgent
}
}
'''
headers = {
'HTTP_USER_AGENT': 'Test Client 1.0',
'HTTP_X_CUSTOM_HEADER': 'custom-value'
}
response = self.query(query, headers=headers)
self.assertResponseNoErrors(response)
request_info = response['data']['requestInfo']
self.assertEqual(request_info['userAgent'], 'Test Client 1.0')
def test_graphql_context(self):
self.client.force_login(self.user)
query = '''
query {
contextInfo {
userId
isAuthenticated
}
}
'''
response = self.query(query)
self.assertResponseNoErrors(response)
context_info = response['data']['contextInfo']
self.assertTrue(context_info['isAuthenticated'])
self.assertEqual(int(context_info['userId']), self.user.id)Install with Tessl CLI
npx tessl i tessl/pypi-graphene-django