0
# Testing Utilities
1
2
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.
3
4
## Capabilities
5
6
### GraphQLTestCase
7
8
Test case base class for GraphQL testing with built-in GraphQL execution and assertion helpers.
9
10
```python { .api }
11
class GraphQLTestCase(django.test.TestCase):
12
"""
13
Test case base class for GraphQL testing.
14
15
Provides utility methods for executing GraphQL queries and mutations
16
in Django test environment with assertion helpers and error handling.
17
"""
18
19
GRAPHQL_SCHEMA = None # GraphQL schema for testing
20
GRAPHQL_URL = '/graphql/' # GraphQL endpoint URL
21
22
def query(self, query, op_name=None, input_data=None, variables=None, headers=None):
23
"""
24
Execute GraphQL query in test environment.
25
26
Parameters:
27
- query: GraphQL query string
28
- op_name: Operation name for named queries
29
- input_data: Input data for mutations (deprecated, use variables)
30
- variables: Query variables
31
- headers: HTTP headers
32
33
Returns:
34
dict: GraphQL response data
35
"""
36
37
def assertResponseNoErrors(self, resp, msg=None):
38
"""
39
Assert GraphQL response has no errors.
40
41
Parameters:
42
- resp: GraphQL response
43
- msg: Custom assertion message
44
45
Raises:
46
AssertionError: If response contains errors
47
"""
48
49
def assertResponseHasErrors(self, resp, msg=None):
50
"""
51
Assert GraphQL response has errors.
52
53
Parameters:
54
- resp: GraphQL response
55
- msg: Custom assertion message
56
57
Raises:
58
AssertionError: If response contains no errors
59
"""
60
61
def setUp(self):
62
"""Set up test case with schema configuration."""
63
64
def _client_query(self, query, op_name=None, input_data=None, variables=None, headers=None):
65
"""
66
Internal method for client query execution.
67
68
Parameters:
69
- query: GraphQL query string
70
- op_name: Operation name
71
- input_data: Input data
72
- variables: Query variables
73
- headers: HTTP headers
74
75
Returns:
76
django.http.HttpResponse: HTTP response
77
"""
78
```
79
80
### Utility Functions
81
82
Helper functions for GraphQL query execution and testing patterns outside of test case classes.
83
84
```python { .api }
85
def graphql_query(query, variables=None, headers=None, client=None, graphql_url='/graphql/'):
86
"""
87
Utility function for making GraphQL requests in tests.
88
89
Parameters:
90
- query: GraphQL query string
91
- variables: Query variables dict
92
- headers: HTTP headers dict
93
- client: Django test client instance
94
- graphql_url: GraphQL endpoint URL
95
96
Returns:
97
django.http.HttpResponse: HTTP response with GraphQL result
98
"""
99
100
def format_graphql_query(query, variables=None, operation_name=None):
101
"""
102
Format GraphQL query with variables and operation name.
103
104
Parameters:
105
- query: GraphQL query string
106
- variables: Query variables
107
- operation_name: GraphQL operation name
108
109
Returns:
110
dict: Formatted query data for HTTP request
111
"""
112
113
def extract_graphql_errors(response):
114
"""
115
Extract GraphQL errors from response.
116
117
Parameters:
118
- response: HTTP response with GraphQL data
119
120
Returns:
121
list: List of GraphQL error objects
122
"""
123
```
124
125
## Usage Examples
126
127
### Basic Query Testing
128
129
```python
130
from graphene_django.utils.testing import GraphQLTestCase
131
from myapp.schema import schema
132
133
class UserQueryTest(GraphQLTestCase):
134
GRAPHQL_SCHEMA = schema
135
136
def setUp(self):
137
self.user = User.objects.create(
138
username='testuser',
139
email='test@example.com'
140
)
141
142
def test_users_query(self):
143
query = '''
144
query {
145
users {
146
id
147
username
148
149
}
150
}
151
'''
152
153
response = self.query(query)
154
self.assertResponseNoErrors(response)
155
156
users = response['data']['users']
157
self.assertEqual(len(users), 1)
158
self.assertEqual(users[0]['username'], 'testuser')
159
160
def test_user_by_id_query(self):
161
query = '''
162
query GetUser($id: ID!) {
163
user(id: $id) {
164
username
165
166
}
167
}
168
'''
169
170
variables = {'id': str(self.user.id)}
171
response = self.query(query, variables=variables)
172
self.assertResponseNoErrors(response)
173
174
user_data = response['data']['user']
175
self.assertEqual(user_data['username'], 'testuser')
176
self.assertEqual(user_data['email'], 'test@example.com')
177
```
178
179
### Mutation Testing
180
181
```python
182
class UserMutationTest(GraphQLTestCase):
183
GRAPHQL_SCHEMA = schema
184
185
def test_create_user_mutation(self):
186
mutation = '''
187
mutation CreateUser($input: CreateUserInput!) {
188
createUser(input: $input) {
189
user {
190
id
191
username
192
193
}
194
errors {
195
field
196
messages
197
}
198
}
199
}
200
'''
201
202
variables = {
203
'input': {
204
'username': 'newuser',
205
'email': 'new@example.com',
206
'password': 'securepassword123'
207
}
208
}
209
210
response = self.query(mutation, variables=variables)
211
self.assertResponseNoErrors(response)
212
213
result = response['data']['createUser']
214
self.assertEqual(len(result['errors']), 0)
215
self.assertEqual(result['user']['username'], 'newuser')
216
217
# Verify user was created in database
218
user = User.objects.get(username='newuser')
219
self.assertEqual(user.email, 'new@example.com')
220
221
def test_create_user_validation_error(self):
222
# Create existing user
223
User.objects.create(username='existing', email='existing@example.com')
224
225
mutation = '''
226
mutation CreateUser($input: CreateUserInput!) {
227
createUser(input: $input) {
228
user {
229
id
230
}
231
errors {
232
field
233
messages
234
}
235
}
236
}
237
'''
238
239
variables = {
240
'input': {
241
'username': 'existing', # This should cause validation error
242
'email': 'new@example.com'
243
}
244
}
245
246
response = self.query(mutation, variables=variables)
247
self.assertResponseNoErrors(response) # No GraphQL errors
248
249
result = response['data']['createUser']
250
self.assertIsNone(result['user'])
251
self.assertGreater(len(result['errors']), 0)
252
self.assertEqual(result['errors'][0]['field'], 'username')
253
```
254
255
### Authentication Testing
256
257
```python
258
from django.contrib.auth import get_user_model
259
from django.test import override_settings
260
261
User = get_user_model()
262
263
class AuthenticatedGraphQLTest(GraphQLTestCase):
264
GRAPHQL_SCHEMA = schema
265
266
def setUp(self):
267
self.user = User.objects.create_user(
268
username='testuser',
269
email='test@example.com',
270
password='testpass123'
271
)
272
273
def test_authenticated_query(self):
274
# Login user
275
self.client.force_login(self.user)
276
277
query = '''
278
query {
279
me {
280
id
281
username
282
283
}
284
}
285
'''
286
287
response = self.query(query)
288
self.assertResponseNoErrors(response)
289
290
me_data = response['data']['me']
291
self.assertEqual(me_data['username'], 'testuser')
292
293
def test_unauthenticated_query_error(self):
294
query = '''
295
query {
296
me {
297
id
298
username
299
}
300
}
301
'''
302
303
response = self.query(query)
304
self.assertResponseHasErrors(response)
305
306
errors = response['errors']
307
self.assertIn('authentication', errors[0]['message'].lower())
308
309
def test_permission_required_mutation(self):
310
# Create staff user
311
staff_user = User.objects.create_user(
312
username='staff',
313
email='staff@example.com',
314
password='staffpass',
315
is_staff=True
316
)
317
318
mutation = '''
319
mutation DeleteUser($id: ID!) {
320
deleteUser(id: $id) {
321
success
322
errors {
323
field
324
messages
325
}
326
}
327
}
328
'''
329
330
# Test without permission
331
self.client.force_login(self.user)
332
response = self.query(mutation, variables={'id': str(self.user.id)})
333
self.assertResponseHasErrors(response)
334
335
# Test with permission
336
self.client.force_login(staff_user)
337
response = self.query(mutation, variables={'id': str(self.user.id)})
338
self.assertResponseNoErrors(response)
339
```
340
341
### Connection/Pagination Testing
342
343
```python
344
class PaginationTest(GraphQLTestCase):
345
GRAPHQL_SCHEMA = schema
346
347
def setUp(self):
348
# Create multiple users for pagination testing
349
for i in range(10):
350
User.objects.create(
351
username=f'user{i}',
352
email=f'user{i}@example.com'
353
)
354
355
def test_connection_pagination(self):
356
query = '''
357
query GetUsers($first: Int, $after: String) {
358
users(first: $first, after: $after) {
359
edges {
360
node {
361
id
362
username
363
}
364
cursor
365
}
366
pageInfo {
367
hasNextPage
368
hasPreviousPage
369
startCursor
370
endCursor
371
}
372
}
373
}
374
'''
375
376
# First page
377
response = self.query(query, variables={'first': 3})
378
self.assertResponseNoErrors(response)
379
380
connection = response['data']['users']
381
self.assertEqual(len(connection['edges']), 3)
382
self.assertTrue(connection['pageInfo']['hasNextPage'])
383
self.assertFalse(connection['pageInfo']['hasPreviousPage'])
384
385
# Second page
386
end_cursor = connection['pageInfo']['endCursor']
387
response = self.query(query, variables={'first': 3, 'after': end_cursor})
388
self.assertResponseNoErrors(response)
389
390
connection = response['data']['users']
391
self.assertEqual(len(connection['edges']), 3)
392
self.assertTrue(connection['pageInfo']['hasPreviousPage'])
393
```
394
395
### Filtering Testing
396
397
```python
398
class FilteringTest(GraphQLTestCase):
399
GRAPHQL_SCHEMA = schema
400
401
def setUp(self):
402
self.active_user = User.objects.create(
403
username='active',
404
email='active@example.com',
405
is_active=True
406
)
407
self.inactive_user = User.objects.create(
408
username='inactive',
409
email='inactive@example.com',
410
is_active=False
411
)
412
413
def test_filter_by_active_status(self):
414
query = '''
415
query GetUsers($isActive: Boolean) {
416
users(isActive: $isActive) {
417
edges {
418
node {
419
username
420
isActive
421
}
422
}
423
}
424
}
425
'''
426
427
# Test active users
428
response = self.query(query, variables={'isActive': True})
429
self.assertResponseNoErrors(response)
430
431
users = response['data']['users']['edges']
432
self.assertEqual(len(users), 1)
433
self.assertEqual(users[0]['node']['username'], 'active')
434
435
# Test inactive users
436
response = self.query(query, variables={'isActive': False})
437
self.assertResponseNoErrors(response)
438
439
users = response['data']['users']['edges']
440
self.assertEqual(len(users), 1)
441
self.assertEqual(users[0]['node']['username'], 'inactive')
442
```
443
444
### Error Handling Testing
445
446
```python
447
class ErrorHandlingTest(GraphQLTestCase):
448
GRAPHQL_SCHEMA = schema
449
450
def test_invalid_query_syntax(self):
451
invalid_query = '''
452
query {
453
users {
454
id
455
nonexistentField
456
}
457
}
458
'''
459
460
response = self.query(invalid_query)
461
self.assertResponseHasErrors(response)
462
463
errors = response.get('errors', [])
464
self.assertGreater(len(errors), 0)
465
466
def test_resolver_exception(self):
467
query = '''
468
query {
469
userThatThrowsError {
470
id
471
}
472
}
473
'''
474
475
response = self.query(query)
476
self.assertResponseHasErrors(response)
477
478
errors = response.get('errors', [])
479
self.assertIn('error', errors[0]['message'].lower())
480
481
def test_custom_error_formatting(self):
482
# Override error formatting for sensitive information
483
with override_settings(DEBUG=False):
484
query = '''
485
query {
486
sensitiveOperation {
487
data
488
}
489
}
490
'''
491
492
response = self.query(query)
493
self.assertResponseHasErrors(response)
494
495
# In production, errors should be generic
496
errors = response.get('errors', [])
497
self.assertNotIn('database', errors[0]['message'].lower())
498
```
499
500
### Custom Headers and Context Testing
501
502
```python
503
class CustomContextTest(GraphQLTestCase):
504
GRAPHQL_SCHEMA = schema
505
506
def test_custom_headers(self):
507
query = '''
508
query {
509
requestInfo {
510
headers
511
userAgent
512
}
513
}
514
'''
515
516
headers = {
517
'HTTP_USER_AGENT': 'Test Client 1.0',
518
'HTTP_X_CUSTOM_HEADER': 'custom-value'
519
}
520
521
response = self.query(query, headers=headers)
522
self.assertResponseNoErrors(response)
523
524
request_info = response['data']['requestInfo']
525
self.assertEqual(request_info['userAgent'], 'Test Client 1.0')
526
527
def test_graphql_context(self):
528
self.client.force_login(self.user)
529
530
query = '''
531
query {
532
contextInfo {
533
userId
534
isAuthenticated
535
}
536
}
537
'''
538
539
response = self.query(query)
540
self.assertResponseNoErrors(response)
541
542
context_info = response['data']['contextInfo']
543
self.assertTrue(context_info['isAuthenticated'])
544
self.assertEqual(int(context_info['userId']), self.user.id)
545
```