{"id":1091,"date":"2020-10-18T19:36:36","date_gmt":"2020-10-18T19:36:36","guid":{"rendered":"http:\/\/10.0.0.14\/?p=1091"},"modified":"2021-09-07T23:32:13","modified_gmt":"2021-09-07T23:32:13","slug":"tdd-with-django-graphene-docker-part-3","status":"publish","type":"post","link":"https:\/\/tutorials.leesonresearch.com\/tutorials\/2020\/10\/18\/tdd-with-django-graphene-docker-part-3\/","title":{"rendered":"TDD with Gatsby, Django &#038; Docker Part 1, Chapter 03 &#8212; Django Graphene"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Django Graphene<\/h2>\n\n\n\n<p>Hopefully, after <a href=\"http:\/\/tdd-with-django-graphene-docker-part-2\">chapter 2<\/a> you&#8217;re getting the hang of test driven development aka TDD. I find it most empowering run tests to see OK. Next up, is setting Django Graphene.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Installation of Django Graphene<\/h4>\n\n\n\n<p>Way back in part 1 of this tutorial you created a requirements.txt file which should include graphene-django==2.2.0 alone with all the other goodies. <\/p>\n\n\n\n<p>If you check if you do have graphene-django installed you can run the following command:<\/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 pip3 freeze\n<\/pre><\/div>\n\n\n<p>This command will print a list of all your installed packages.<\/p>\n\n\n\n<p>If you don&#8217;t find graphene-django in your requirements.txt you can update \/server\/requirements.txt with the following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [3]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nDjango&gt;=3.0\npsycopg2&gt;=2.8.6\ngraphene-django&gt;=2.2.0\ndjango-autoslug&gt;=1.9.6\ndjango-filter&gt;=2.0.0\ndjango-graphql-jwt&gt;=0.1.5\ndjango-mptt&gt;=0.11.0\nPillow&gt;=6.1.0\ndjango-cors-headers&gt;=3.1.0\ndjango-jwt-auth&gt;=0.0.2\nPyJWT&gt;=1.7.1\ncoverage&gt;=5.1\nfreezegun&gt;=0.3.15\npython-dateutil&gt;=2.8.1\npytest-django&gt;=3.9.0\n<\/pre><\/div>\n\n\n<p>If you did have to add graphene-django to the requirements.txt we need to rebuild our server again to install graphene-django along with all its dependencies:<\/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 build server\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Configuring Django Graphene<\/h4>\n\n\n\n<p>In the todo-app-graphql\/server\/todo_proj\/settings.py file find INSTALLED_APPS and add the following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [10]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nINSTALLED_APPS = &#x5B;\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'todo_app',\n    'mptt',\n    'graphene_django',\n]\n<\/pre><\/div>\n\n\n<p>At the very bottom of the settings.py file add:<\/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\">\n# todo-app-graphql\/server\/todo_proj\/settings.py\n# ... code\nGRAPHENE = {\n  'SCHEMA': 'todo_proj.schema.schema',\n}\n<\/pre><\/div>\n\n\n<p>Now your todo-app-graphql\/server\/todo_proj\/settings.py should look like the following. Note the highlighted changes we just made.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [44,139,140,141]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/settings.py\n\n&quot;&quot;&quot;\nDjango settings for todo_proj project.\n\nGenerated by 'django-admin startproject' using Django 3.2.5.\n\nFor more information on this file, see\nhttps:\/\/docs.djangoproject.com\/en\/3.2\/topics\/settings\/\n\nFor the full list of settings and their values, see\nhttps:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/\n&quot;&quot;&quot;\n\nfrom pathlib import Path\n\n# Build paths inside the project like this: BASE_DIR \/ 'subdir'.\nBASE_DIR = Path(__file__).resolve().parent.parent\n\n\n# Quick-start development settings - unsuitable for production\n# See https:\/\/docs.djangoproject.com\/en\/3.2\/howto\/deployment\/checklist\/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = 'django-insecure-z6a#15u#4bw092uhpnvhbc_h!p$!&amp;amp;&amp;amp;g&amp;amp;qvb5dq3vj7kj3@#%bg'\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = True\n\nALLOWED_HOSTS = &#x5B;]\n\n\n# Application definition\n\nINSTALLED_APPS = &#x5B;\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'todo_app',\n    'mptt',\n    'graphene_django',\n]\n\nMIDDLEWARE = &#x5B;\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\nROOT_URLCONF = 'todo_proj.urls'\n\nTEMPLATES = &#x5B;\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': &#x5B;],\n        'APP_DIRS': True,\n        'OPTIONS': {\n            'context_processors': &#x5B;\n                'django.template.context_processors.debug',\n                'django.template.context_processors.request',\n                'django.contrib.auth.context_processors.auth',\n                'django.contrib.messages.context_processors.messages',\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = 'todo_proj.wsgi.application'\n\n\n# Database\n# https:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/#databases\n\nimport os\n\nDATABASES = { \n'default': \n  { \n    'ENGINE': 'django.db.backends.postgresql', \n    'NAME': os.environ.get('DATABASE_NAME'), \n    'USER': os.environ.get('DATABASE_USER'), \n    'PASSWORD': os.environ.get('DATABASE_PASSWORD'),    \n    'HOST': os.environ.get('DATABASE_HOST'), \n    'PORT': os.environ.get('DATABASE_PORT'), \n  } \n}\n\n\n# Password validation\n# https:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/#auth-password-validators\n\nAUTH_PASSWORD_VALIDATORS = &#x5B;\n    {\n        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\n    },\n]\n\n\n# Internationalization\n# https:\/\/docs.djangoproject.com\/en\/3.2\/topics\/i18n\/\n\nLANGUAGE_CODE = 'en-us'\n\nTIME_ZONE = 'UTC'\n\nUSE_I18N = True\n\nUSE_L10N = True\n\nUSE_TZ = True\n\n\n# Static files (CSS, JavaScript, Images)\n# https:\/\/docs.djangoproject.com\/en\/3.2\/howto\/static-files\/\n\nSTATIC_URL = '\/static\/'\n\n# Default primary key field type\n# https:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/#default-auto-field\n\nDEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'\n\nGRAPHENE = {\n  'SCHEMA': 'todo_proj.schema.schema',\n}\n<\/pre><\/div>\n\n\n<p>We will need to reboot the server for these changes to take effect:<\/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 down\ndocker-compose up -d\n\n# if you run into problems with the above you can start the services separately insuring that the database is up and running before we boot up the server\n\ndocker-compose down\ndocker-compose up -d database\ndocker-compose up -d server\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">The Wonders of GraphiQL<\/h4>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/graphql\/graphiql\" target=\"_blank\">GraphiQL<\/a> is our graphical interactive playground in-browser GraphQL IDE. How cool is that.<\/p>\n\n\n\n<p>Note that we will need to disable <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.djangoproject.com\/en\/2.0\/ref\/csrf\/\" target=\"_blank\">Django CSRF protection<\/a>.<\/p>\n\n\n\n<p>Install GraphiQL by adding the highlighted lines below to todo_proj\/urls.py file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [5,6,10]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/urls.py\n\nfrom django.contrib import admin\nfrom django.urls import path\nfrom django.views.decorators.csrf import csrf_exempt\nfrom graphene_django.views import GraphQLView\n\nurlpatterns = &#x5B;\n    path('admin\/', admin.site.urls),\n    path('graphql\/', csrf_exempt(GraphQLView.as_view(graphiql=True))),\n]\n<\/pre><\/div>\n\n\n<p>Now we should be able to access GraphiQL at <a href=\"http:\/\/localhost:5555\/graphql\/\">http:\/\/localhost:5555\/graphql\/<\/a><\/p>\n\n\n\n<p>Add the following to GraphiQL and we should a list of all the todos we have entered from the Django admin UI or the Django shell.<\/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\">\nquery {\n  todos {\n    id\n    title\n    task\n  }\n}\n<\/pre><\/div>\n\n\n<p>We should then see&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"615\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/08\/graphql_todos-1-1024x615.png\" alt=\"\" class=\"wp-image-2326\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/08\/graphql_todos-1-1024x615.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/08\/graphql_todos-1-300x180.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/08\/graphql_todos-1-768x462.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/08\/graphql_todos-1-600x361.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/08\/graphql_todos-1-945x568.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/08\/graphql_todos-1.png 1514w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Writing Tests for Django-Graphene<\/h4>\n\n\n\n<p><a href=\"http:\/\/tdd-with-django-graphene-docker-part-2\">In our last post<\/a> we wrote tests for the model confirming, (or not), that we were able to read and add data from the postgres db. For the next step lets write tests for listing our data from graphql.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Structure of a Graphene Test<\/h4>\n\n\n\n<p>We are going to be doing the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Import GraphQLTestCase<\/li><li>Import our models<\/li><li>import json to process in python,<a rel=\"noreferrer noopener\" href=\"https:\/\/www.laurencegellert.com\/2018\/09\/django-tricks-for-processing-and-storing-json\/\" target=\"_blank\"> (more on Django + JSON)<\/a><\/li><li>Create a class where we pass in the GraphQLTestCase class<ul><li>Create a function to setup our data<\/li><li>Create a function to test listing all todos<\/li><li>Assert that there are no errors in our response<\/li><\/ul><\/li><\/ul>\n\n\n\n<p>Create a new server\/todo_app\/tests\/test_graphql_queries.py file:<\/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\">\ntouch server\/todo_app\/tests\/test_graphql_queries.py\n<\/pre><\/div>\n\n\n<p>&#8230; and add the following tests which test for returning all todos, projects and categories as well as getting specific todo, project and category by id:<\/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\">\n# server\/todo_app\/tests\/test_graphql_queries.py\n\nfrom graphene_django.utils.testing import GraphQLTestCase\nfrom todo_app.models import Todo, Project, Category\nimport json\n\nclass QueryTestCases(GraphQLTestCase):\n  def test_all_todos(self):\n    response = self.query(\n      '''\n      query GetTodos {\n        todos {\n          id\n          title\n          task\n          slug\n          project {\n            id\n            name\n            slug\n          }\n          category {\n            id\n            name\n            slug\n          }\n          \n        }\n      }\n      '''\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_todo_by_id_query(self):\n    proj1 = Project.objects.create(name='proj1')\n    cat1 = Category.objects.create(name='cat1')\n    todo1 = Todo.objects.create(id=1, title='todo1', task='todo1 task', project=proj1, category=cat1)\n    \n    response = self.query(\n      '''\n        query Todo($id: Int!) {\n          todo(id:$id) {\n            id\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        'id': 1\n      }\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_all_projects(self):\n    proj1 = Project.objects.create(name='proj1')\n    cat1 = Category.objects.create(name='cat1')\n    todo1 = Todo.objects.create(title='todo1', task='todo1 task', project=proj1, category=cat1)\n\n    response = self.query(\n      '''\n        query GetProjects {\n            projects {\n              id\n              name\n              slug\n              todoProject {\n                id\n                title\n                task\n                slug\n                category {\n                  id\n                  name\n                  slug\n                }\n              }\n            }\n          }\n      '''\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_project_by_id_query(self):\n    proj1 = Project.objects.create(id=1, name='proj1')\n    cat1 = Category.objects.create(name='cat1')\n    todo1 = Todo.objects.create(title='todo1', task='todo1 task', project=proj1, category=cat1)\n\n    response = self.query(\n      '''\n        query Project($id: Int!) {\n          project(id:$id) {\n            id\n            name\n            slug\n            todoProject {\n              id\n              title\n              task\n              slug\n              category {\n                id\n                name\n                slug\n              }\n            }\n          }\n        }\n      ''',\n      variables = {\n        'id': 1\n      }\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_all_categories_query(self):\n\n    response = self.query(\n      '''\n        query GetCategories {\n          categories {\n            id\n            name\n            todos {\n              id\n              title\n              project {\n                id\n                name\n              }\n            }\n          }\n        }\n      '''\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\n  def test_get_category_by_id(self):\n    cat1 = Category.objects.create(id=1, name='cat1')\n    proj1 = Project.objects.create(id=1, name='proj1')\n    todo = Todo.objects.create(id=1, title='todo', task='todo task', project=proj1)\n\n    response = self.query(\n      '''\n        query GetCategory($id: Int!) {\n          category(id: $id) {\n            id\n            name\n            slug\n            todos {\n              id\n              title\n              task\n              slug\n              project {\n                id\n                name\n                slug\n              }\n            }\n          }\n        }\n      ''',\n      variables = {\n        'id': 1\n      }\n    )\n\n    content = json.loads(response.content)\n    self.assertResponseNoErrors(response)\n\ufeff\n<\/pre><\/div>\n\n\n<p>If we were to run our tests now they would fail since we don&#8217;t have a schema for Graphene, so now we create our schemas.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Creating Our First Type &amp; Schema<\/h4>\n\n\n\n<p>A <strong>Type<\/strong> is an object that may contain multiple <strong>fields<\/strong>. A collection of types is called a <strong>schema<\/strong>.<\/p>\n\n\n\n<p>Every <strong>schema<\/strong> has a type called <strong>query<\/strong> for pulling data from our server and another type called <strong>mutation<\/strong> for sending data to our server.<\/p>\n\n\n\n<p>This is a way over-simplified explanation of GraphQL concepts but it&#8217;s enough to keep plowing on through with. Want to dig into the nitty gritty go <a rel=\"noreferrer noopener\" href=\"http:\/\/graphql.org\/learn\/schema\/\" target=\"_blank\">here<\/a>.<\/p>\n\n\n\n<p><strong>Now we create our todo_app\/schema.py file:<\/strong><\/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\">\ntouch server\/todo_app\/schema.py\n<\/pre><\/div>\n\n\n<p>&#8230; and populate the schema.py with the following:<\/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\">\n# todo-app-graphql\/server\/todo_app\/schema.py\n\nimport graphene\nfrom graphene import Schema\nfrom graphene_django import DjangoObjectType\nfrom graphql import GraphQLError\nfrom todo_app.models import Todo, Project, Category\n\nclass TodoType(DjangoObjectType):\n  class Meta:\n    model = Todo\n\nclass ProjectType(DjangoObjectType):\n  class Meta:\n    model = Project\n\nclass CategoryType(DjangoObjectType):\n  class Meta:\n    model = Category\n\nclass Query(graphene.ObjectType):\n  \n  # todo queries\n  todos = graphene.List(TodoType)\n  def resolve_todos(self, info, **kwargs):\n    return Todo.objects.all()\n\n  todo = graphene.Field(\n    TodoType,\n    id=graphene.Int(),\n  ) \n  def resolve_todo(self, info, id):\n    return Todo.objects.get(pk=id)\n\n  # project queries\n  projects = graphene.List(ProjectType)\n  def resolve_projects(self, info, **kwargs):\n    return Project.objects.all()\n\n  project = graphene.Field(\n    ProjectType,\n    id=graphene.Int(),\n  )\n  def resolve_project(self, info, id):\n    return Project.objects.get(pk=id)\n\n  # category queries\n  categories = graphene.List(CategoryType)\n  def resolve_categories(self, info, **kwargs):\n    return Category.objects.all()\n\n  category = graphene.Field(\n    CategoryType,\n    id=graphene.Int(),\n  )\n  def resolve_category(self, info, id):\n    return Category.objects.get(pk=id)\n\nschema = Schema(query=Query)\n<\/pre><\/div>\n\n\n<p>Above we created a TodoType using the DjangoObjectType which is a custom type from Graphene Django.<\/p>\n\n\n\n<p>We also created a special type Query with a resolver for the field tasks, which returns all our Todo tasks.<\/p>\n\n\n\n<p>Then we extended the above to definitions for the ProjectType and the CategoryType.<\/p>\n\n\n\n<p>Now we create our <strong>todo_proj\/schema.py<\/strong> file <\/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\">\ntouch server\/todo_proj\/schema.py\n<\/pre><\/div>\n\n\n<p>&#8230; and populate with the following <strong>Query<\/strong> type:<\/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\">\n# todo-app-graphql\/server\/todo_proj\/schema.py\n\nimport graphene\n\nimport todo_app.schema\n\nclass Query(\n  todo_app.schema.Query,\n  graphene.ObjectType\n):\n  pass\n\nschema = graphene.Schema(query=Query)\n<\/pre><\/div>\n\n\n<p>The above snippet inherits the query we defined in our todo_app. This allows us to keep all our app schemas isolated in their respective apps.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Running Our First PyTest<\/h4>\n\n\n\n<p>In Part 1 among the many <strong>server\/requirements.txt<\/strong> Django packages we installed was <strong>pytest-django&gt;=3.9.0<\/strong>. I prefer PyTest to Django test because its cleaner, gives more feedback on the status of the tests whether they succeed or fail.<\/p>\n\n\n\n<p>With our following files in place:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>\/server\/todo_app\/tests\/<strong>test_graphql.py<\/strong><\/li><li>\/server\/todo_app\/<strong>schema.py<\/strong><\/li><li>\/server\/todo_proj\/<strong>schema.py<\/strong><\/li><\/ul>\n\n\n\n<p>&#8230; we can now run all our tests including the graphene query test using the pytest command: <\/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&#x5B;+] Running 1\/0\n \u283f Container postgres  Running                                                                             0.0s\n============================================= test session starts ==============================================\nplatform linux -- Python 3.9.6, pytest-6.2.4, py-1.10.0, pluggy-0.13.1\ndjango: settings: todo_proj.settings (from env)\nrootdir: \/server\nplugins: django-4.4.0\ncollected 11 items                                                                                             \n\ntodo_app\/tests\/test_graphql_queries.py ......                                                            &#x5B; 54%]\ntodo_app\/tests\/test_models.py .....                                                                      &#x5B;100%]\n\n=============================================== warnings summary ===============================================\n..\/usr\/local\/lib\/python3.9\/site-packages\/django\/apps\/registry.py:91\n  \/usr\/local\/lib\/python3.9\/site-packages\/django\/apps\/registry.py:91: RemovedInDjango41Warning: 'mptt' defines default_app_config = 'mptt.apps.MpttConfig'. Django now detects this configuration automatically. You can remove default_app_config.\n    app_config = AppConfig.create(entry)\n\n-- Docs: https:\/\/docs.pytest.org\/en\/stable\/warnings.html\n======================================== 11 passed, 1 warning in 4.14s =========================================\n<\/pre><\/div>\n\n\n<p>Success! If we had run into some failures we can use the pytest verbose commands:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>docker-compose run server pytest -v<\/strong> # which displays failures in detail<\/li><li><strong>docker-compose run server pytest -vv<\/strong> # which displays failures in even more detail<\/li><\/ul>\n\n\n\n<p>Next post we <a href=\"\/2020\/07\/09\/todo-app-with-gatsby-django-graphene-docker-pt-4\">introduce mutaions.<\/a><\/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-2\">&lt; Previous: Chapter 2<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-django-graphene-docker-part-4\">Next: Chapter 4 &gt;<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Using test driven development, (TDD) with Django Graphene and Docker.<\/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":[19],"class_list":["post-1091","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-django","category-featured-tutorial","tag-graphene"],"_links":{"self":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1091","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=1091"}],"version-history":[{"count":60,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1091\/revisions"}],"predecessor-version":[{"id":2508,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1091\/revisions\/2508"}],"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=1091"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1091"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1091"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}