{"id":1044,"date":"2020-10-07T22:31:00","date_gmt":"2020-10-07T22:31:00","guid":{"rendered":"http:\/\/10.0.0.14\/?p=1044"},"modified":"2021-10-16T00:54:26","modified_gmt":"2021-10-16T00:54:26","slug":"todo-app-with-gatsby-django-graphene-docker-pt-6","status":"publish","type":"post","link":"https:\/\/tutorials.leesonresearch.com\/tutorials\/2020\/10\/07\/todo-app-with-gatsby-django-graphene-docker-pt-6\/","title":{"rendered":"TDD with Gatsby, Django &#038; Docker Part 1, Chapter 06 &#8212; Testing Django Mutations with JWT"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Django JWT Testing<\/h2>\n\n\n\n<p>Since we implemented JWT authentication in our <a href=\"http:\/\/todo-app-with-gatsby-django-graphene-docker-pt-5\">previous post<\/a> all our mutation tests will fail. Why? Because our server\/todo_app\/schema.py is now testing to confirm that an authenticated user is logged in. After all, we don&#8217;t want anybody to be able to mess with our data. Only authenticated users should have permission to create, edit or update todo app posts which we have accomplished using the create a category mutation as an example below:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">class CreateCategory(graphene.Mutation):\n  id = graphene.Int()\n  name = graphene.String()\n  slug = graphene.String()\n  parent = graphene.Field(CategoryType)\n  <span style=\"color: red; font-weight: bold;\">posted_by = graphene.Field(UserType)<\/span>\n\n  class Arguments:\n    name = graphene.String()\n    parentSlug = graphene.String()\n\n  def mutate(self, info, name, parentSlug=None):\n    <span style=\"color: red; font-weight: bold;\">user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to create new todos!')<\/span>\n\n    if parentSlug:\n      parent = Category.objects.get(slug=parentSlug)\n      category = Category.objects.create(name=name, parent=parent, <span style=\"color: red; font-weight: bold;\">posted_by=user<\/span>)\n    else:\n      category = Category.objects.create(name=name, <span style=\"color: red; font-weight: bold;\">posted_by=user<\/span>)\n\n    return CreateCategory(\n      id=category.id,\n      name=category.name,\n      slug=category.slug,\n      parent=category.parent,\n      <span style=\"color: red; font-weight: bold;\">posted_by=category.posted_by,<\/span>\n    )\n<\/pre>\n\n\n\n<p>Now we need to update our mutation tests so the user&#8217;s web token is included with our GraphQL request. We can obtain our web token by using get_token from graphql_jwt.shortcuts and GraphQLTestCase from graphene_django.utils.testing.<\/p>\n\n\n\n<p>Let&#8217;s dive into the nuts and bolts by updating our create category test: server\/todo_app\/tests\/test_graphql_mutations.py:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># server\/todo_app\/tests\/test_graphql_mutations.py\n\ndef test_create_category_no_parent(self):\n    response = self.query(\n      '''\n      mutation createCategory($name: String!) {\n        createCategory(name: $name) {\n          id\n          name\n          slug\n        }\n      }\n      '''<span style=\"color: red; font-weight: bold;\">, #1\n      variables = {\n        'name': 'my category',\n      }, #2\n      headers = self.headers,<\/span>\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n<\/pre>\n\n\n\n<p>Notice the additions from our first version.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Variables<\/li><li>Headers<\/li><\/ol>\n\n\n\n<p><a href=\"http:\/\/tdd-with-django-graphene-docker-part-4\">In chapter 4<\/a>, we had yet to implement user authentication with JWT. Now we have JWT authentication in place so, if we were to run pytest now the mutation tests would fail because JWT requires the user&#8217;s token to be passed in the headers of the request.<\/p>\n\n\n\n<p>In the above test I&#8217;ve added variables to the mix for the sake of completeness. For the above test to pass we still need to add a setUp method to our MutationTestCases class:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># server\/todo_app\/tests\/test_graphql_mutations.py\n\n# code ...\n\nclass MutationTestCases(GraphQLTestCase):\n\n  def setUp(self):\n    self.user = get_user_model().objects.create(username='ron')\n    self.token   = get_token(self.user)\n    self.headers = {\"HTTP_AUTHORIZATION\": f\"JWT {self.token}\"}\n\n# code ...\n\ndef test_create_category_no_parent(self):\n    response = self.query(\n      '''\n      mutation createCategory($name: String!) {\n        createCategory(name: $name) {\n          id\n          name\n          slug\n        }\n      }\n      ''', #1\n      variables = {\n        'name': 'my category',\n      }, #2\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Updating Mutation Tests to Pass JWT Authentication<\/h4>\n\n\n\n<p>If we were to run our pytest now, this create category test would pass while the remaining mutation tests would fail. To refactor all our tests in order to pass JWT authentication we need to add the variables and headers to the remaining tests which I have done with the updated tests below:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># server\/todo_app\/tests\/test_graphql_mutations.py\n\nfrom graphql_jwt.shortcuts import get_token\nfrom django.contrib.auth import get_user_model\nfrom graphene_django.utils.testing import GraphQLTestCase\nfrom todo_app.models import Todo, Project, Category\nimport json\n\nclass MutationTestCases(GraphQLTestCase):\n\n  def setUp(self):\n    self.user = get_user_model().objects.create(username='ron')\n    self.token   = get_token(self.user)\n    self.headers = {\"HTTP_AUTHORIZATION\": f\"JWT {self.token}\"}\n\n  def test_create_todo(self):\n    proj1 = Project(name='proj1')\n    proj1.save()\n\n    cat1 = Category(name='cat1')\n    cat1.save()\n\n    response = self.query(\n      '''\n      mutation createTodo($title: String!, $task: String!, $projId: Int!, $catId: Int!) {\n        createTodo(title: $title, task: $task, projId: $projId, catId: $catId) {\n          title\n          task\n          slug\n          project {\n            name\n            slug\n          }\n          category {\n            name\n            slug\n          }\n        }\n      }\n      ''',\n      variables = {\n        'title': 'todo',\n        'task': 'task',\n        'projId': proj1.id,\n        'catId':  cat1.id,\n      },\n      headers=self.headers,\n    )\n\n    expected = {\n      'data': {\n        'createTodo': {\n          'title': 'todo',\n          'task': 'task',\n          'slug': 'todo',\n          'project': {\n            'name': 'proj1',\n            'slug': 'proj1',\n          },\n          'category': {\n            'name': 'cat1',\n            'slug': 'cat1',\n          }\n        }\n      }\n    }\n\n    content = json.loads(response.content)\n    assert content ==  expected\n    self.assertResponseNoErrors(response)\n\n  def test_update_todo_name(self):\n    # Test for updating a todos name\n\n    proj1 = Project(name='proj1')\n    proj1.save()\n\n    cat1 = Category(name='cat1')\n    cat1.save()\n\n    todo = Todo(title='todo', task='todo task', project=proj1, category=cat1, posted_by=self.user)\n    todo.save()\n\n    response = self.query(\n      '''\n      mutation updateTodo($id: Int!, $title: String!) {\n        updateTodo(id: $id, title: $title){\n          id\n          slug\n          title\n          task\n          postedBy {\n            id\n            username\n            email\n          }\n          project {\n            slug\n            name\n          }\n          category {\n            slug\n            name\n            parent {\n              slug\n              name\n            }\n          }\n        }\n      }\n\n      ''',\n        variables = {\n          'id': todo.id,\n          'title': 'todo updated',\n        },\n        headers = self.headers,\n      )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_todo_task(self):\n    # Test for updating a todos task\n\n    proj1 = Project(name='proj1')\n    proj1.save()\n\n    cat1 = Category(name='cat1')\n    cat1.save()\n\n    todo = Todo(title='todo', task='todo task', project=proj1, category=cat1, posted_by=self.user)\n    todo.save()\n\n    response = self.query(\n        '''\n        mutation updateTodo($id: Int!, $task: String!) {\n          updateTodo(id: $id, task: $task){\n            id\n            slug\n            title\n            task\n            project {\n              slug\n              name\n            }\n            category {\n              slug\n              name\n              parent {\n                slug\n                name\n              }\n            }\n          }\n        }\n\n        ''',\n          variables = {\n            'id': todo.id,\n            'task': 'todo updated task',\n          },\n          headers=self.headers,\n        )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_todo_project(self):\n    # Test for updating a todos project\n\n    proj1 = Project(name='proj1')\n    proj1.save()\n\n    cat1 = Category(name='cat1')\n    cat1.save()\n\n    todo = Todo(title='todo', task='todo task', project=proj1, category=cat1, posted_by=self.user)\n    todo.save()\n\n    proj2 = Project(name='proj2')\n    proj2.save()\n\n    response = self.query(\n        '''\n        mutation updateTodo($id: Int!, $projId: Int!) {\n          updateTodo(id: $id, projId: $projId){\n            id\n            slug\n            title\n            task\n            project {\n              slug\n              name\n            }\n            category {\n              slug\n              name\n              parent {\n                slug\n                name\n              }\n            }\n          }\n        }\n\n        ''',\n          variables = {\n            'id': todo.id,\n            'projId': proj2.id,\n          },\n          headers=self.headers,\n        )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_todo_category(self):\n    # Test for updating a todos category\n\n    proj1 = Project(name='proj1')\n    proj1.save()\n\n    cat1 = Category(name='cat1')\n    cat1.save()\n\n    todo = Todo(title='todo', task='todo task', project=proj1, category=cat1, posted_by=self.user)\n    todo.save()\n\n    cat2 = Category(name='cat2')\n    cat2.save()\n\n    response = self.query(\n        '''\n        mutation updateTodo($id: Int!, $catId: Int!) {\n          updateTodo(id: $id, catId: $catId){\n            id\n            slug\n            title\n            task\n            project {\n              slug\n              name\n            }\n            category {\n              slug\n              name\n              parent {\n                slug\n                name\n              }\n            }\n          }\n        }\n\n        ''',\n          variables = {\n            'id': todo.id,\n            'catId': cat2.id,\n          },\n          headers=self.headers,\n        )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_todo_is_completed(self):\n    # Test for updating a todos is_completed status\n\n    proj1 = Project(name='proj1')\n    proj1.save()\n\n    cat1 = Category(name='cat1')\n    cat1.save()\n\n    todo = Todo(title='todo', task='todo task', project=proj1, category=cat1, posted_by=self.user)\n    todo.save()\n\n\n    response = self.query(\n        '''\n        mutation updateTodo($id: Int!, $isCompleted: Boolean!) {\n          updateTodo(id: $id, isCompleted: $isCompleted){\n            id\n            slug\n            title\n            task\n            isCompleted\n            project {\n              slug\n              name\n            }\n            category {\n              slug\n              name\n              parent {\n                slug\n                name\n              }\n            }\n          }\n        }\n\n        ''',\n          variables = {\n            'id': todo.id,\n            'isCompleted': True,\n          },\n          headers=self.headers,\n        )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_todo_everything(self):\n    # Test for updating all of a todo's attributes at once\n\n    proj1 = Project(name='proj1')\n    proj1.save()\n\n    cat1 = Category(name='cat1')\n    cat1.save()\n\n    proj2 = Project(name='proj2')\n    proj2.save()\n\n    cat2 = Category(name='cat2')\n    cat2.save()\n\n    todo = Todo(title='todo', task='todo task', project=proj1, category=cat1, posted_by=self.user)\n    todo.save()\n\n\n    response = self.query(\n        '''\n        mutation updateTodo(\n          $id: Int!, \n          $title: String!, \n          $task: String!, \n          $projId: Int!, \n          $catId: Int!, \n          $isCompleted: Boolean!\n          ) {\n          updateTodo(\n            id: $id, \n            title: $title, \n            task: $task, \n            projId: $projId, \n            catId: $catId, \n            isCompleted: $isCompleted,\n            ){\n            id\n            slug\n            title\n            task\n            isCompleted\n            project {\n              slug\n              name\n            }\n            category {\n              slug\n              name\n              parent {\n                slug\n                name\n              }\n            }\n          }\n        }\n\n        ''',\n          variables = {\n            'id': todo.id,\n            'title': 'updated title',\n            'task': 'updated task',\n            'projId': proj2.id,\n            'catId': cat2.id,\n            'isCompleted': True,\n          },\n          headers=self.headers,\n        )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_create_project(self):\n    response = self.query(\n      '''\n      mutation createProject($name: String!, $description: String!) {\n        createProject(name: $name, description: $description) {\n          id\n          name\n          description\n          slug\n        }\n      }\n      ''',\n      variables = {\n        'name': 'My Project',\n        'description': 'Description for my project',\n      },\n      headers = self.headers\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_project_name(self):\n    project = Project.objects.create(name='foo', posted_by=self.user)\n    # Test updating name of a project\n\n    response = self.query(\n      '''\n      mutation updateProject($id: Int!, $name: String!) {\n        updateProject(id:$id, name: $name) {\n          id\n          name\n          description\n          slug\n        }\n      }\n      ''',\n      variables = {\n        'id': project.id,\n        'name': 'My Project',\n      },\n      headers = self.headers\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_project_description(self):\n    # Test updating of project description\n    project = Project.objects.create(name='foo', description='bar', posted_by=self.user)\n\n    response = self.query(\n      '''\n      mutation updateProject($id: Int!, $description: String!) {\n        updateProject(id:$id, description: $description) {\n          id\n          name\n          description\n          slug\n        }\n      }\n      ''',\n      variables = {\n        'id': project.id,\n        'description': 'Description for my project',\n      },\n      headers = self.headers\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_project_name_description(self):\n    project = Project.objects.create(name='foo', posted_by=self.user)\n    # Test updating both name and description of a project\n\n    response = self.query(\n      '''\n      mutation updateProject($id: Int!, $name: String!, $description: String!) {\n        updateProject(id:$id, name: $name, description: $description) {\n          id\n          name\n          description\n          slug\n        }\n      }\n      ''',\n      variables = {\n        'id': project.id,\n        'name': 'My Project',\n        'description': 'Description for my project',\n      },\n      headers = self.headers\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_delete_project(self):\n    project = Project.objects.create(name='foo', posted_by=self.user)\n\n    response = self.query(\n      '''\n      mutation deleteProject($id: Int!) {\n        deleteProject(id: $id) {\n          id\n          name\n        }\n      }\n      ''',\n      variables = {\n        'id': project.id,\n      },\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_create_category_no_parent(self):\n    response = self.query(\n      '''\n      mutation createCategory($name: String!) {\n        createCategory(name: $name) {\n          id\n          name\n          slug\n        }\n      }\n      ''',\n      variables = {\n        'name': 'my category',\n      },\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_create_category_with_parent(self):\n    parent = Category.objects.create(name='Category Parent', posted_by=self.user)\n\n    response = self.query(\n      '''\n      mutation createCategory($name: String!, $parentId: Int!) {\n        createCategory(name: $name, parentId: $parentId) {\n          id\n          name\n          slug\n        }\n      }\n      ''',\n      variables = {\n        'name': 'my category',\n        'parentId': parent.id\n      },\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n  \n  def test_update_category_name(self):\n    category = Category.objects.create(name='foo', posted_by=self.user)\n    # Test for changing a category's name\n\n    response = self.query(\n      '''\n      mutation updateCategory($id: Int!,$name: String!){\n        updateCategory(id: $id, name: $name) {\n          id\n          name\n          slug\n        }\n      }\n      ''',\n      variables = {\n        'id': category.id,\n        'name': 'New Category Name',\n      },\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_category_parent(self):\n    categoryParent = Category.objects.create(name='Parent Category', posted_by=self.user)\n    category = Category.objects.create(name='foo', posted_by=self.user)\n    # Test for moving category into a parent category.\n\n    response = self.query(\n      '''\n      mutation updateCategory($id: Int!,$parentId: Int!){\n        updateCategory(id: $id, parentId: $parentId) {\n          id\n          name\n          slug\n          parent {\n            id\n            name\n            slug\n          }\n        }\n      }\n      ''',\n      variables = {\n        'id': category.id,\n        'parentId': categoryParent.id,\n      },\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_update_category_name_parent(self):\n    categoryParent = Category.objects.create(name='category parent', posted_by=self.user)\n    category = Category.objects.create(name='foobar', posted_by=self.user)\n    # Test for changing a category's name &amp; moving renamed category into a parent category\n    \n    response = self.query(\n      '''\n      mutation updateCategory($id: Int!, $name: String!, $parentId: Int!){\n        updateCategory(id: $id, name: $name, parentId: $parentId) {\n          id\n          name\n          slug\n          parent {\n            id\n            name\n            slug\n          }\n        }\n      }\n      ''',\n      variables = {\n        'id': category.id,\n        'name': 'New Category Name',\n        'parentId': categoryParent.id\n      },\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_delete_category(self):\n    category = Category.objects.create(name='foo', posted_by=self.user)\n\n    response = self.query(\n      '''\n      mutation deleteCategory($id: Int!) {\n        deleteCategory(id: $id) {\n          id\n          name\n        }\n      }\n      ''',\n      variables = {\n        'id': category.id,\n      },\n      headers = self.headers,\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n<\/pre>\n\n\n\n<p>We are ready to run the updated mutation tests:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server pytest\n\nCreating todo_mptt_4_server_run ... done\n========================================== test session starts ===========================================\nplatform linux -- Python 3.8.5, pytest-6.1.1, py-1.9.0, pluggy-0.13.1\ndjango: settings: todo_proj.settings (from env)\nrootdir: \/var\/www\/server\nplugins: django-4.1.0\ncollected 29 items                                                                                       \n\ntodo_app\/tests\/test_graphql_mutations.py ..................                                        &#x5B; 62%]\ntodo_app\/tests\/test_graphql_queries.py ......                                                      &#x5B; 82%]\ntodo_app\/tests\/test_models.py .....                                                                &#x5B;100%]\n\n============================================ warnings summary ============================================\n..\/..\/..\/usr\/local\/lib\/python3.8\/site-packages\/graphene\/types\/field.py:2\n  \/usr\/local\/lib\/python3.8\/site-packages\/graphene\/types\/field.py:2: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working\n    from collections import Mapping, OrderedDict\n\n..\/..\/..\/usr\/local\/lib\/python3.8\/site-packages\/graphene\/relay\/connection.py:2\n  \/usr\/local\/lib\/python3.8\/site-packages\/graphene\/relay\/connection.py:2: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working\n    from collections import Iterable, OrderedDict\n\n..\/..\/..\/usr\/local\/lib\/python3.8\/site-packages\/mptt\/signals.py:8\n  \/usr\/local\/lib\/python3.8\/site-packages\/mptt\/signals.py:8: RemovedInDjango40Warning: The providing_args argument is deprecated. As it is purely documentational, it has no replacement. If you rely on this argument as documentation, you can move the text to a code comment or docstring.\n    node_moved = ModelSignal(providing_args=&#x5B;\n\n..\/..\/..\/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/signals.py:3\n  \/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/signals.py:3: RemovedInDjango40Warning: The providing_args argument is deprecated. As it is purely documentational, it has no replacement. If you rely on this argument as documentation, you can move the text to a code comment or docstring.\n    token_issued = Signal(providing_args=&#x5B;'request', 'user'])\n\n..\/..\/..\/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/signals.py:4\n  \/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/signals.py:4: RemovedInDjango40Warning: The providing_args argument is deprecated. As it is purely documentational, it has no replacement. If you rely on this argument as documentation, you can move the text to a code comment or docstring.\n    token_refreshed = Signal(providing_args=&#x5B;'request', 'user'])\n\n..\/..\/..\/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/refresh_token\/signals.py:3\n  \/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/refresh_token\/signals.py:3: RemovedInDjango40Warning: The providing_args argument is deprecated. As it is purely documentational, it has no replacement. If you rely on this argument as documentation, you can move the text to a code comment or docstring.\n    refresh_token_revoked = Signal(providing_args=&#x5B;'request', 'refresh_token'])\n\n..\/..\/..\/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/refresh_token\/signals.py:4\n  \/usr\/local\/lib\/python3.8\/site-packages\/graphql_jwt\/refresh_token\/signals.py:4: RemovedInDjango40Warning: The providing_args argument is deprecated. As it is purely documentational, it has no replacement. If you rely on this argument as documentation, you can move the text to a code comment or docstring.\n    refresh_token_rotated = Signal(\n\n-- Docs: https:\/\/docs.pytest.org\/en\/stable\/warnings.html\n===================================== 29 passed, 7 warnings in 1.56s =====================================\n\n<\/pre><\/div>\n\n\n<p>The tests pass. Success! If the tests didn&#8217;t pass you can troubleshoot the error with:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Running pytest in verbose mode with the -vv flag. <\/li><li>Test the failing query in GraphiQL. If the GraphiQL query executes successfully this implies that the issue is not related to the schema.<\/li><li>Since <strong>self.assertResponseNoErrors(response)<\/strong> will only tell you that the test returns error code 400 and nothing else, I like to use a hack to force pytest to print out the query result by <strong>adding assert content != {}<\/strong> above <strong>self.assertResponseNoErrors(response)<\/strong>. The test will fail but the query result will be printed to the terminal, (when pytest is in verbose mode), which should tell you a lot.<\/li><\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Conclusion<\/h4>\n\n\n\n<p>Where are we now? In:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Chapter 1 we setup our development environment for <ul><li>Docker <\/li><li>Django Graphene<\/li><\/ul><\/li><li>Chapter 2 we used TDD in developing our <ul><li>model tests <\/li><li>and models necessary to pass our model tests<\/li><li>how to use the Django shell to load data <\/li><li>configure Django Admin to manipulate our data<\/li><\/ul><\/li><li>Chapter 3 we <ul><li>installed and configured Django Graphene,<\/li><li>setup our schemas<\/li><li>configured GraphiQL for passing GraphQL queries to the server.<\/li><\/ul><\/li><li>Chapter 4 we introduced mutations using TDD to define <ul><li>all our mutations<\/li><li>updated our schemas to accommodate all CRUD operations.<\/li><\/ul><\/li><li>Chapter 5 we implemented JWT user authentication.<\/li><li>Chapter 6 we updated our mutation tests to JWT user authentication.<\/li><\/ul>\n\n\n\n<p>To finish off this tutorial, <a href=\"http:\/\/tdd-with-django-graphene-docker-part-7-backup-db-makefile\">next chapter<\/a> we backup our database using Docker and populate our Makefile.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-django-graphene-docker-part-5-jwt\">&lt; Previous:  Chapter 5<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-django-graphene-docker-part-7-backup-db-makefile\">Next: Chapter 7 &gt;<\/a><\/div>\n<\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Django JWT Testing Since we implemented JWT authentication in our previous post all our mutation tests will fail. Why? Because our server\/todo_app\/schema.py is now testing to confirm that an authenticated user is logged in. After all, we don&#8217;t want anybody&#8230; <a class=\"more-link\" href=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/2020\/10\/07\/todo-app-with-gatsby-django-graphene-docker-pt-6\/\">Continue Reading &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":312,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,20],"tags":[43,15,30,29,35],"class_list":["post-1044","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-django","category-featured-tutorial","tag-authentication","tag-django","tag-jwt","tag-tdd","tag-unit-testing"],"_links":{"self":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1044","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1044"}],"version-history":[{"count":46,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1044\/revisions"}],"predecessor-version":[{"id":2604,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1044\/revisions\/2604"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media\/312"}],"wp:attachment":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1044"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1044"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1044"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}