Blog



  • Creating a Django/React Application with TDD Part 1

    Created: June 19, 2017, 4:02 p.m.

    React
    Django
    TDD
    Django REST

    Introduction

    This is my first tutorial, so bare with me. There are a lot of tutorials out there for setting up React with Django, so why am I writing this one? Well the answer is I have not seen any of them implement unit testing with it. Although, other guides serve as a "Quick and easy way to get started", mine will be adding unit testing. Also, I will try to make this guide simple and easy. This guide will be building a TODO app in 2 parts:

    1. Building our API with the Django Rest Framework wtihTDD
    2. Implementing a React within our project with TDD

    I also offer a completed github repository of everything in this part of the tutorial. Check it out here

    About Me

    If you don't know me, let me introduce myself. I am Matthew Dickens a Software, Web, and Android developer who at the time of posting recently graduated from the University of Houston. I have been doing Web Development for over 4 years and working with Django for 3 years. I am also skilled in a variety of things such as Android Development. If you wanna know more feel free to visit my website. Also, at the time of this post, I am looking for work so if you know of any potential positions, please let me know.

    Part 1: Building our API with the Django Rest Framework and TDD


    Starting our Django Project

    Django offers tons of tools and libraries that you can use at your disposal. It is one of my favorite web frameworks and I use it a lot. It helps developers develop applications quickly, it is flexible and offers a variety of security options. This guide assumes that you have Python 3.6 with pip installed. If you don't, there are plenty of guides online that tell you how to. So first of all, let us start our Django Application! Open your terminal and type the following bash commands in whatever directory you want your project to be:

    1. Install the necessary tools first
    $ pip install virtualenv django
    
    1. Create a directory for your project
    $ mkdir react_django
    
    1. Move to that directory
    $ cd react_django
    
    1. Use the django-admin startproject command to create your project inside that directory(Dont forget the dot at the end)
    $ django-admin startproject react_django .  
    

    Now your project should look like this:

    ./react_django
    |- /react_django
    |-- __init__.py 
    |-- settings.py # Django settings 
    |-- urls.py # Base urls.py file for routes
    |-- wsgi.py
    |- manage.py # Helps manage your django application
    

    If it looks like this, congrats! You are ready for the next steps in setting up our application.

    Creating our virtual environment and installing all requirements

    Now let us create a virtual environment to isolate our project from the global site-packages:

    $ virtualenv venv
    

    Now your project should look like this:

    ./react_django
    |- /react_django
    |-- __init__.py 
    |-- settings.py # Django settings 
    |-- urls.py # Base urls.py file for routes
    |-- wsgi.py
    |- /venv # Virtual enviorment directory
    |- manage.py # Helps manage your django application
    

    Lets put that virtual environment to use by activating it. I have provided the commands for both Windows and Unix based operating systems:

    • Windows:
    $ source venv/Scripts/activate
    
    • Linux/OSX:
    $ source venv/bin/activate
    

    Now that we have activated our virtual environment, it is time to get the libraries we need for this project. First, we need to make a file called requirements.txt

    (venv) $ touch requirements.txt
    

    Some of you know where I am going with this, but within the file, put the following content within it:

    requirements.txt

    Django
    djangorestframework
    django-webpack-loader
    wheel
    coverage
    

    As for those who don't know, requirements.txt is a list of libraries that are required to be installed by our environment. By putting this command in the terminal

    pip install -r requirements.txt
    

    pip will recursively install every library listed within the requirements.txt file. This gives you the ability to work with others or move your project to different machines without having to remember which libraries to use. I will explain what each library listed in the requirements.txt is and what it serves for our project:

    • Django
    • The backend framework we are using to build our application
    • Source
    • djangorestframework
    • Another framework that provides a powerful and flexible toolkit for building APIs within Django
    • Source
    • django-webpack-loader
    • You will see why we installed this more in the second part of this tutorial. Basically, this library helps us to keep track of the bundles that webpack generates for us.
    • Source
    • wheel
    • A built-package format for Python. You don't really need to know anything about this one. It just does cool things that are a bit complex for this tutorial.
    • coverage
    • We will use this to provide us code coverage when unit testing our backend. It offers a lot of neat features and it what I use personally all the time.
    • selenium
    • Selenium helps us automate the browser for testing purposes. Think of it as a tool to enable us to test site behavior as a client.

    Editing the settings.py file

    Now that we have everything installed, its time to get the settings.py within the react_django directory sorted out. Here is what the settings.py file should look like

    ./react_django/settings.py

    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = <YOUR_SECRET_KEY>
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = []
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        #Third Party Libraries
        'rest_framework', #The library used to serve our api
        #Local Apps
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'react_django.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')], #This is where our single html file will be.
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'react_django.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
    """
    For this tutorial, we are just going to use sqlite. You can use whatever database you want,
    but just make sure you create a test database.
    """
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
            'TEST': {
                #This is so that we have a separate database for our unit testing
                'NAME': os.path.join(BASE_DIR, 'db_test.sqlite3')
            }
        }   
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/1.11/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    
    STATIC_URL = '/static/'
    
    

    Adding a Templates Directory with our single HTML file

    Now, if you notice in the settings.py file, I had this for the TEMPLATES setting:

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')], #This is where our single html file will be.
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

    This tells django to look for html within a templates directory at the base directory of our project. So lets add that directory:

    $ mkdir templates
    

    And then add a file called index.html to our templates directory which will serve the single HTML file our React application will use:

    $ touch templates/index.html
    

    Now your project should look like this:

    ./react_django
    |- /react_django
    |-- __init__.py 
    |-- settings.py # Django settings 
    |-- urls.py # Base urls.py file for routes
    |-- wsgi.py
    |- /venv # Virtual enviorment directory
    |- /templates
    |-- index.html
    |- manage.py
    

    Setting up and writing our first functional test

    Setup for our functional test

    Some of you may be thinking, why the are we even using selenium, isn't that going to be tested with our front-end tests? Well, React is not serving the HTML nor the JS, but Django is. So we need to write tests to ensure that we are serving the correct content needed for our application to run on the front-end.

    First of all, we need a few things. A driver and a web browser that is compatible with the driver. For the purposes of this tutorial, we will be using the ChromeDriver along with the Google Chrome browser. But you can use other drivers like the gekodriver for Firefox if you wish.

    To install the driver, download the latest version that is compatible with your OS here. After you have done that extract the zip, there should be a file called chromedriver that is extracted from the zip file. Now you need to put it somewhere on your system path:

    • for Unix based OS's, put it in ~/.local/bin
    • for Windows, put it in your Python Scripts folder

    To test to see if its working, simply do this:

    $ chromedriver --version
    ChromeDriver 2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41)
    

    Now, if you don't already, download and install google chrome here.

    Implementing our functional test

    Now, its time to test! First we need to create a directory for our functional test along with our tests.py file:

    $ mkdir functional_tests
    $ touch functional_tests/__init__.py
    $ touch functional_tests/tests.py
    

    Now your project should look like this:

    ./react_django
    |- /react_django
    |-- __init__.py 
    |-- settings.py # Django settings 
    |-- urls.py # Base urls.py file for routes
    |-- wsgi.py
    |- /venv # Virtual enviorment directory
    |- /templates
    |-- index.html
    |- /functional_tests
    |-- tests.py
    |- manage.py
    

    Next, lets start our functional test by editing the file we just created:

    django_react/functional_tests/tests.py

    from django.test import LiveServerTestCase
    from selenium import webdriver
    
    class FunctionalTest(LiveServerTestCase):
    
        #Our setup method which sets the browser to Chrome
        def setUp(self):
            self.browser = webdriver.Chrome()
    
        def tearDown(self):
            self.browser.quit()
    
        def test_page_loads_with_react_app(self):
            #Gets the URL of the first page and requests it
            self.browser.get(self.live_server_url)
            #Checks if React Django is in the browser title
            self.assertIn('React Django', self.browser.title)
            #Checks if the <div id="app"></div> exists
            react_app_element = self.browser.find_element_by_id('app')
            self.assertIsNotNone(react_app_element)
    

    Now, lets run the test!

    $ python manage.py test
    F
    ======================================================================
    FAIL: test_page_loads_div_with_id_app (functional_tests.tests.FunctionalTest)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "C:\Users\madmin\Desktop\react_django\functional_tests\tests.py", line 18, in test_page_loads_div_with_id_app
        self.assertIn('React Django', self.browser.title)
    AssertionError: 'React Django' not found in ''
    
    ----------------------------------------------------------------------
    Ran 1 test in 7.384s
    
    FAILED (failures=1)
    Creating test database for alias 'default'...
    Destroying old test database for alias 'default'...
    System check identified no issues (0 silenced).
    Destroying test database for alias 'default'...
    

    Great! We have a failing test! Now let us write the minimum viable code to get it to pass. First of all, we don't have our HTML wired up to our main urls.py file. So let us do that:

    react_django/react_django/urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from django.views.generic import TemplateView
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^$', TemplateView.as_view(template_name='index.html'))
    ]
    

    Next, we need to write the HTML to get this to pass:

    react_django/templates/index.html

    <html>
        <head>
            <title>React Django</title>
        </head>
        <body>
            <div id="app"></div>
        </body>
    </html>
    

    And when we run our tests:

    $ python manage.py test
    .
    ----------------------------------------------------------------------
    Ran 1 test in 7.241s
    
    OK
    Creating test database for alias 'default'...
    Destroying old test database for alias 'default'...
    System check identified no issues (0 silenced).
    Destroying test database for alias 'default'...
    

    Yay! We have a passing test! Now we can move on with building our API.

    Building our API with TDD

    In this tutorial, we will build a simple TODO list app. So, let us think what we need to do in order to design this API. Let's think of the following user stories:

    • As a user, I would want to see a list of things to do that have not been done
    • As a user, I want to add an item to my TODO list
    • As a user, I want to be able to edit an item on my TODO list
    • As a user, I want to be able to delete an item on my TODO list
    • As a user, I want to be able to mark an item as done
    • As a user, I want to see a list of all items including items that have been complete

    Great! This gives us a sense of what features we want to implement. Now, lets start off by creating our app which will handle our TODO List API. Enter the following command in the terminal:

    $ python manage.py startapp todo_list_api
    

    Once you run this, your project should look like this:

    ./react_django
    |- /react_django
    |-- __init__.py 
    |-- settings.py # Django settings 
    |-- urls.py # Base urls.py file for routes
    |-- wsgi.py
    |- /venv
    |- /templates
    |-- index.html
    |- /functional_tests
    |-- tests.py
    |- /todo_list_api
    |-- /migrations
    |-- __init__.py
    |-- admin.py
    |-- apps.py
    |-- models.py
    |-- tests.py
    |-- views.py
    |- manage.py
    

    Now, let us not forget to Obey The Testing Goat and write our tests for our API first:

    react_django/todo_list_api/tests.py

    import json
    
    from django.urls import reverse
    from django.http import JsonResponse
    
    from rest_framework import status
    from rest_framework.test import APITestCase
    
    from .models import Item
    
    
    class TodoListApiTest(APITestCase):
        """
        A Test Suite in which tests our API
        """
    
        def setUp(self):
            """
            The setUp method for the TodoListAPI unit tests
            """
            Item.objects.create(name="Test Item", done=False)
            Item.objects.create(name='Done Item', done=True)
    
        def tearDown(self):
            """
            The
            :return None:
            """
            Item.objects.filter(name__exact="Test Item").delete()
            Item.objects.filter(name__exact="Done Item").delete()
    
        def test_get_all_items(self):
            """
            A test in which tests if you can get all items whether they are done or not
            """
            url = reverse('todo-list')
            data = {
                'all': 'y'
            }
            response = self.client.get(url, data, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(json.loads(response.content.decode('utf8')),
                             [
                                 {'id': 1, 'name': 'Test Item', 'done': False},
                                 {'id': 2, 'name': 'Done Item', 'done': True}
                             ])
    
        def test_get_all_not_done_items(self):
            url = reverse('todo-list')
            response = self.client.get(url)
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(json.loads(response.content.decode('utf8')),
                             [{'id': 1, 'name': 'Test Item', 'done': False}])
    
        def test_create_item(self):
            """
            A test in which tests item creation
            """
            url = reverse('todo-list')
            data = {
                'name': 'Thing To Do',
                'done': False
            }
            response = self.client.post(url, data, format='json')
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
            self.assertEqual(Item.objects.count(), 3)
            self.assertEqual(Item.objects.get(name__exact='Thing To Do').name, 'Thing To Do')
            self.assertEqual(Item.objects.get(name__exact='Thing To Do').done, False)
    
        def test_get_single_item(self):
            """
            A test which tests the APIs ability to get a single item
            """
            url = reverse('todo-item', kwargs={'pk': 1})
            response = self.client.get(url, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(json.loads(response.content.decode('utf8')),
                             {'id': 1, 'name': 'Test Item', 'done': False})
    
        def test_mark_item_as_done_and_edit_name(self):
            """
            A test in which marks an item as done
            """
            url = reverse('todo-item', kwargs={'pk': 1})
            data = {
                'name': 'Clean my room',
                'done': True
            }
            response = self.client.put(url, data, format='json')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertEqual(json.loads(response.content.decode('utf8')),
                             {'id': 1, 'name': 'Clean my room', 'done': True})
    
        def test_deleting_an_item(self):
            """
            A test for deleting an item
            """
            Item.objects.create(name='Deleted Item', done=False)
            url = reverse('todo-item', kwargs={'pk': 3})
            response = self.client.delete(url)
            self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
            self.assertFalse(Item.objects.filter(pk=3).count())
    

    Wow, that's a lot of tests! But for the sake of keeping this tutorial as short as possible, I wanted to write all the tests ahead of time. When you run the tests here, it should fail automatically with tons of syntax errors and import errors. But, think of unit testing as a "shopping list" you write down all the things you need the code to do. So let's write the code to make all these tests pass. According to our tests, our model has mainly 2(except the ID) pieces of data. So our "shopping list" says we need a model to hold a name and whether or not that item is done. So let us create a model that does that:

    react_django/todo_list_api/models.py

    from django.db import models
    
    
    class Item(models.Model):
        name = models.CharField(max_length=150, default="")
        done = models.BooleanField(default=False)
    

    Then we need to make migrations and migrate. This is so we can have a migrations file within the migrations directory and migrate our default database from our settings.py:

    (venv) $ python manage.py makemigrations
    (venv) $ python manage.py migrate
    

    Awesome! Now, since we are working with the rest framework, we need to create ourselves a ModelSerializer for this model. To do that, let us first create a file called serializers.py within our app directory and while we are at it make a urls.py file too:

    $ touch todo_list_api/serializers.py && touch todo_list_api/urls.py
    

    Now your app directory should look like this:

    |- /todo_list_api
    |-- /migrations
    |-- admin.py
    |-- apps.py
    |-- models.py
    |-- serializers.py
    |-- tests.py
    |-- urls.py
    |-- views.py
    

    Cool beans! Now let us do some magic on that serializers.py file we just made:

    from rest_framework import serializers
    from .models import Item
    
    
    class ItemSerializer(serializers.ModelSerializer):
        class Meta:
            model = Item
            fields = ('id', 'name', 'done')
    

    Didn't expect so little code did you? The REST Framework makes creating a working API very easy! Now that we have a serializer for our Item model, let us add our views in the views.py file:

    react_django/todo_list_api/views.py

    from django.http import JsonResponse, HttpResponse
    from rest_framework import status
    from rest_framework.parsers import JSONParser
    
    from .models import Item
    from .serializers import ItemSerializer
    
    
    def todo_list(request):
        """
        A list API view for the TODO list
        :param request:
        :return HttpResponse|JsonResponse:
        """
        if request.method == 'GET':
            is_all = request.GET.get('all')
            if is_all == 'y':
                items = Item.objects.all()
            else:
                items = Item.objects.filter(done=False)
            serializer = ItemSerializer(items, many=True)
            return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK)
        elif request.method == 'POST':
            data = JSONParser().parse(request)
            serializer = ItemSerializer(data=data)
            if serializer.is_valid():
                serializer.save()
                return JsonResponse(serializer.data, status=status.HTTP_201_CREATED)
        return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
    def todo_item(request, pk):
        """
        A single item API view
        :param request:
        :param pk:
        :return HttpResponse|JsonResponse:
        """
        try:
            item = Item.objects.get(pk=pk)
        except Item.DoesNotExist:
            return HttpResponse(status=404)
    
        if request.method == 'GET':
            serializer = ItemSerializer(item, many=False)
            return JsonResponse(serializer.data, status=status.HTTP_200_OK)
        elif request.method == 'PUT':
            data = JSONParser().parse(request)
            serializer = ItemSerializer(data=data, instance=item)
            if serializer.is_valid():
                serializer.save()
                return JsonResponse(serializer.data, status=200)
            return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        elif request.method == 'DELETE':
            item.delete()
            return JsonResponse({'success': True}, status=status.HTTP_204_NO_CONTENT)
    

    In our tests, it says we need a list API view and a single item API view. So we needed to create 2 views todo_item and todo_list. After that, all we gotta do is just hook it up with the todo_list_api/urls.py and the react_djagno/urls.py files:

    todo_list_api/urls.py

    from django.conf.urls import url
    from .views import todo_list, todo_item
    
    urlpatterns = [
        url(r'^items/$', todo_list, name='todo-list'),
        url(r'^item/(?P<pk>[0-9]+)', todo_item, name='todo-item')
    ]
    

    react_django/urls.py

    from django.conf.urls import url, include #Add include in import statement
    from django.contrib import admin
    from django.views.generic import TemplateView
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^$', TemplateView.as_view(template_name='index.html')),
        url(r'^api/', include('todo_list_api.urls')) #Add this line
    ]
    

    Now, let us run our tests again:

    (venv) $ python manage.py test
    Creating test database for alias 'default'...
    Destroying old test database for alias 'default'...
    System check identified no issues (0 silenced).
    .......
    ----------------------------------------------------------------------
    Ran 7 tests in 7.720s
    
    OK
    Destroying test database for alias 'default'...
    

    Awesome! Now let's see the fruits of our labor and check the code coverage. Remember when we installed that coverage library? Wel will now set that up by adding our .coveragerc file to the base directory our project:

    $ touch .coveragerc
    

    Now the project should look like this:

    ./react_django
    |- /functional_tests
    |- /react_djagno
    |- /templates
    |- /todo_list_api
    |- /venv
    |- .coveragerc
    |- db.sqlite3
    |- manage.py
    |- requirements.txt
    

    Once you created this file, edit it so it looks like this:

    ./react_django/.coveragerc

    [run]
    branch = True
    source = .
    
    [report]
    exclude_lines =
      pragma: no cover
    
        # Don't complain about missing debug-only code:
        def __unicode__
        def __repr__
        if self\.debug
    
        # Don't complain if tests don't hit defensive assertion code:
        raise AssertionError
        raise NotImplementedError
    
        # Don't complain if non-runnable code isn't run:
        if 0:
        if __name__ == .__main__.:
    
    omit =
        react_django/*
        */__init__.py
        functional_tests/*
        */migrations/*
        */site-packages/*
        */Lib/*
        */lib/*
        manage.py
        */tests.py
        */urls.py
        venv/*
    
    show_missing = True
    

    Great! Now, to run the tests with code coverage enter this command:

    (venv) $ coverage run manage.py test
    

    This will generate a .coverage file that looks like a bunch of gibberish. In order to see the coverage report in a clean formatted way, just enter this command:

    (venv) $ coverage report
    Name                           Stmts   Miss Branch BrPart  Cover   Missing
    --------------------------------------------------------------------------
    todo_list_api\admin.py             1      0      0      0   100%
    todo_list_api\apps.py              3      3      0      0     0%   1-5
    todo_list_api\models.py            4      0      0      0   100%
    todo_list_api\serializers.py       6      0      0      0   100%
    todo_list_api\views.py            38      4     16      4    85%   29, 41-42, 53, 23->29, 26->29, 50->53, 54->exit
    --------------------------------------------------------------------------
    TOTAL                             52      7     16      4    84%
    

    Whoa, looks like we are missing some lines in our views.py file. Let's try to fix that. Add these tests to the tests.py file:

    ./react_django/todo_list_api/tests.py

        def test_create_item_with_invalid_data(self):
            """
            A test in which tests creating an item with invalid data
            """
            url = reverse('todo-list')
            data = {
                'name': False,
                'done': 'Some Text'
            }
            response = self.client.post(url, data, format='json')
            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
    
        def test_get_invalid_item(self):
            url = reverse('todo-item', kwargs={'pk': 200})
            response = self.client.get(url, format='json')
            self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
    
        def test_edit_item_with_invalid_data(self):
            url = reverse('todo-item', kwargs={'pk': 1})
            data = {
                'name': False,
                'done': 'Some Text'
            }
            response = self.client.put(url, data, format='json')
            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
    

    Now run the tests again with coverage:

    (venv) $ coverage run manage.py test
    Creating test database for alias 'default'...
    Destroying old test database for alias 'default'...
    System check identified no issues (0 silenced).
    ..........
    ----------------------------------------------------------------------
    Ran 10 tests in 7.312s
    
    OK
    Destroying test database for alias 'default'...
    
    (venv) $ coverage report
    Name                           Stmts   Miss Branch BrPart  Cover   Missing
    --------------------------------------------------------------------------
    todo_list_api\admin.py             1      0      0      0   100%
    todo_list_api\apps.py              3      3      0      0     0%   1-5
    todo_list_api\models.py            4      0      0      0   100%
    todo_list_api\serializers.py       6      0      0      0   100%
    todo_list_api\views.py            38      0     16      2    96%   23->29, 54->exit
    --------------------------------------------------------------------------
    TOTAL                             52      3     16      2    93%
    
    

    Now we have increased our coverage by 11% in our views.py file. But the reason why we are at 96% right now is that there can never be a time where those lines jump to the end of the function. So let's change a few things up. Add these tests to the tests.py file:

    ./react_django/todo_list_api/tests.py

        def test_send_bad_request_to_todo_list_view(self):
            url = reverse('todo-list')
            response = self.client.delete(url)
            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
    
        def test_send_bad_request_to_todo_item_view(self):
            url = reverse('todo-item', kwargs={'pk': 1})
            response = self.client.post(url)
            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
    

    Run the test and it will fail again saying that the serializer is not initialized. Why not move the JsonResponses with the errors as the parameters for the data to where makes more sense sense. Modify your views.py to look like this:

    ./react_django/todo_list_api/views.py

    def todo_list(request):
        """
        A list API view for the TODO list
        :param request:
        :return HttpResponse|JsonResponse:
        """
        if request.method == 'GET':
            is_all = request.GET.get('all')
            if is_all == 'y':
                items = Item.objects.all()
            else:
                items = Item.objects.filter(done=False)
            serializer = ItemSerializer(items, many=True)
            return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK)
        elif request.method == 'POST':
            data = JSONParser().parse(request)
            serializer = ItemSerializer(data=data)
            if serializer.is_valid():
                serializer.save()
                return JsonResponse(serializer.data, status=status.HTTP_201_CREATED)
            return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
    
    def todo_item(request, pk):
        """
        A single item API view
        :param request:
        :param pk:
        :return HttpResponse|JsonResponse:
        """
        try:
            item = Item.objects.get(pk=pk)
        except Item.DoesNotExist:
            return HttpResponse(status=404)
    
        if request.method == 'GET':
            serializer = ItemSerializer(item, many=False)
            return JsonResponse(serializer.data, status=status.HTTP_200_OK)
        elif request.method == 'PUT':
            data = JSONParser().parse(request)
            serializer = ItemSerializer(data=data, instance=item)
            if serializer.is_valid():
                serializer.save()
                return JsonResponse(serializer.data, status=200)
            return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        elif request.method == 'DELETE':
            item.delete()
            return JsonResponse({'success': True}, status=status.HTTP_204_NO_CONTENT)
        return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
    

    Let us run our tests with coverage again and see what the result is:

    (venv) $ coverage run manage.py test
    Creating test database for alias 'default'...
    Destroying old test database for alias 'default'...
    System check identified no issues (0 silenced).
    ............
    ----------------------------------------------------------------------
    Ran 12 tests in 7.337s
    
    OK
    Destroying test database for alias 'default'...
    
    (venv) $ coverage report
    Name                           Stmts   Miss Branch BrPart  Cover   Missing
    --------------------------------------------------------------------------
    todo_list_api\admin.py             1      0      0      0   100%
    todo_list_api\apps.py              3      3      0      0     0%   1-5
    todo_list_api\models.py            4      0      0      0   100%
    todo_list_api\serializers.py       6      0      0      0   100%
    todo_list_api\views.py            40      0     16      0   100%
    --------------------------------------------------------------------------
    TOTAL                             54      3     16      0    96%
    

    Huzzah! 100% coverage in the views.py file. All we had to do is change the code to make sure every branch is ran. If you so desire to cover the apps.py file, just add this to your tests.py:

    ./react_django/todo_list_api/tests.py

        def test_app_name_todo_list_api(self):
            self.assertEqual(TodoListApiConfig.name, 'todo_list_api')
    

    Although, it is not necessarily needed, unless you are a perfectionist like me. You should have this after you do this:

    (venv) $ coverage run manage.py test
    Creating test database for alias 'default'...
    Destroying old test database for alias 'default'...
    System check identified no issues (0 silenced).
    .............
    ----------------------------------------------------------------------
    Ran 13 tests in 7.322s
    
    OK
    Destroying test database for alias 'default'...
    
    (venv) $ coverage report
    
    Name                           Stmts   Miss Branch BrPart  Cover   Missing
    --------------------------------------------------------------------------
    todo_list_api\admin.py             1      0      0      0   100%
    todo_list_api\apps.py              3      0      0      0   100%
    todo_list_api\models.py            4      0      0      0   100%
    todo_list_api\serializers.py       6      0      0      0   100%
    todo_list_api\views.py            40      0     16      0   100%
    --------------------------------------------------------------------------
    TOTAL                             54      0     16      0   100%
    
    

    If you see this, congratulations! You have scaffolded a working API with unit testing! In the next part of this tutorial, we will do the same thing only with using ReactJS. So what did we do in this tutorial:

    1. We set up a Django Rest environment
    2. We made a functional test to check all the requirements that React needs for later
    3. We created a Restful API using TDD using Django, Django rest framework, and coverage tools

    Stay tuned for part 2! Follow me on twitter for more updates. If you have any questions, please leave a comment below. Thanks for reading!

  • Bash Scripting Shortcuts: Pushing All Changes in Git

    Created: Jan. 28, 2017, 6:17 p.m.

    bash
    git

    Introduction

    I cannot count how many times I entered these commands:

    git add -A
    git commit -m "Some commit message" #Sorry I don't use vi to write commits
    git push origin master
    

    I thought there has got to be a way to make this easier when I just want to commit everything into my master branch. Then I remembered, "Hey, why not write a bash script?". So that is what I did.

    The Script

    COMMIT_MESSAGE = $1 #Accepts a string argument ./push_everything.sh "Commit Message"
    git add -A
    git commit -m COMMIT_MESSAGE
    git push origin master
    

    And whala! We have a script that instantly commits everything into the master branch. You may even extend this script by doing this.

    COMMIT_MESSAGE = $1
    BRANCH_TO_PUSH = $2
    git add -A
    git commit -m COMMIT_MESSAGE
    git push origin BRANCH_TO_PUSH
    

    This one enables you to choose which branch to push to: ./push_everything.sh "Commit Message" <Branch>

  • A Simple WorkAround For regenerate session_id Errors When Testing Zend Controllers

    Created: Jan. 20, 2017, 9:46 p.m.

    zend
    php
    phpunit

    I have gotten this error many many times whenever I tested a controller in Zend Framework 2 using the AbstractHttpsControllerTestCase class:

    session_regenerate_id(): Cannot regenerate session id - headers already sent
    

    So how did I fix it? Well after hours, maybe weeks, of searching I simply just did this.

    phpunit --stderr
    

    And it finally worked!

    Thanks almostbalanced blog for this!

    Link to article

  • Hello World! Welcome to My Blog!

    Created: Jan. 3, 2017, 10:57 p.m.

    main

    Introduction

    I am excited to announce the creation of my own blog. My name is Matthew, or you can call me Matt, and I am a Software Developer. Currently, as of the time I posted this, I am senior undergraduate student at the University of Houston who loves all things about Technology, Software Development, Video Games, and Star Wars("May the force be with you").

    Why did you start a blog?

    The idea of it started when I did a workshop on my own for the University of Houston students on Android Development. We all know when you have to do a presentation such as a workshop you have to prepare. But as I was preparing for the workshop I felt more enlightened and learned more on the basics of Android Development than I ever did. I felt I can learn more by teaching and sharing information to others. From that experience I decided I wanted to make a blog to help others learn and in turn for me to learn as well.

    What will this blog be about?

    Tutorials, Tips/Tricks, Software Engineering, and anything I can think of that will be useful for my readers and myself. This blog serves as "Developer Notes" as it for everyone. The goal of this blog is to learn while teaching. So if you have a suggestion, post a comment down below!

    Conclusion

    I hope everyone who reads this learns something and/or is enlightened by my thoughts or opinions. Hopefully this will be helpful for both me and you(the reader). Please feel free to write comments on anything I said, mention mistakes I have made, or just say hello. Also blogger allows you to subscribe to please feel free to subscribe as well. But just forewarning, I will post irregularly because I am a student. I will try to come up with a posting schedule as soon I can, after all this is just a hobby not a job. Thank you for reading! I look forward to learning by sharing the things I learned with everyone.

Subscribe to the Newsletter