Skip to content

Assignment 5

Dan Carr edited this page Mar 6, 2017 · 1 revision

Assignment 5 - DRF Fun - Testing

Prerequisites

  • Student has Github account
  • Student has Docker installed.
  • Adequate bandwidth. This is not a cafe exercise.
  • Student has completed Assignment 3

Concepts Introduced

  • Unit Testing and TDD
  • Docker Containers
  • Implement Django Rest Framework API

In the previous assignment we set up a basic API using DRF.

In this assignment we'll go over how we can use a TDD approach to build our app.

Step 0 - Pull the latest version of our class repo

git clone https://github.com/hackoregon/hacku-devops-2017.git

Go to the assignment directory

cd hacku-devops-2017/assign5

Step 1 - Validate that our tests are working

With TDD, as we write code, we use unit tests to verify that it is working as we expect.

Django automatically already creates a test file for us in user/tests.py

Let's auto-run our tests in the docker container:

docker-compose run web sniffer

Whenever we change code we'll get instant feedback letting us know if we've broken anything.

The way we've set it up, DRF is currently serving the following urls:

GET /users/
GET /users/{pk}
POST /users/
DELETE /user/{pk}

Setup some tests in user/tests.py to verify that this is working:

from django.test import TestCase, Client
from django.contrib.auth.models import User, AnonymousUser
from django.core.urlresolvers import reverse

# Create your tests here.

class UserAPITestCase(TestCase):

    """
    User API
    """

    def setUp(self):
        self.c = Client()

        self.normal_user = User.objects.create_user(
            username="joe", password="password", email="[email protected]")
        self.superuser = User.objects.create_superuser(
            username="clark", password="supersecret", email="[email protected]")

    def test_can_get_user_list(self):
        """GET /user returns a list of users"""
        assert False is True

    def test_get_user_returns_correct_fields(self):
        """GET /user/{pk} returns a user"""
        assert False is True

    def tearDown(self):
        for user in User.objects.all():
            user.delete()
  • setUp() is run before each test. Here, we create two users. One normal, and one superuser.
    • This is a major advantage of unit testing - the abiliy to reliably create a known environment. For example, in this case, we know that our database has exactly 2 users.
  • tearDown() is run after each test. In this case we are making sure that we delete any users which might have been created. This isn't necessarily necessary since Django clears that database between each test anyway. Still. it doesn't hurt ;)
  • This is a blank scaffolding which describes the functionality that we expect from our API. Obviously if we were to run this, all the tests would fail (because we have assert False is True in each test.

Watch the output in your terminal.

Step 2 - Test that we can return a list of users

GET /users/

In user/tests.py change def test_can_get_user_list test function to:

    def test_can_get_user_list(self):
        """GET /user returns a list of users"""
        url = reverse("user-list")
        response = self.c.get(url)

        assert response.status_code == 200, \
            "Expect 200 OK. got: {}" . format(response.status_code)
        num_users = len(response.json())

        assert num_users == 2, \
            "Expect it to return exactly 2 users. Got: {}" . format(num_users)

Watch the output in your terminal.

Notes:

  • self.c is the Test Client which we setup in setUp(). This is a Django utility which helps for testing views.
  • We make a request to GET /users/ using the Test Client.
  • We validate that we get a 200 OK response
  • We make sure that it returns a list of users which is exectly 2 in length (because we know there are exactly 2 users in the database)
  • We use Django's reverse() to get the url.

Step 3 - Test that we can get the right fields

GET /users/{pk}/

In user/tests.py change def test_get_user_returns_correct_fields test function to:

def test_get_user_returns_correct_fields(self):
        """GET /user/{pk} returns a user"""

        expected_fields = ['url', 'username', 'email', 'is_staff']
        url = reverse("user-detail", args=[self.normal_user.pk])
        response = self.c.get(url)

        assert response.status_code == 200, \
            "Expect 200 OK. got: {}" . format(response.status_code)

        assert response.json()["is_staff"] == False
        assert response.json()["username"] == "joe"
        assert response.json()["email"] == "[email protected]"
        expected_url = "http://testserver/users/{}/" . format(
            self.normal_user.pk)
        assert response.json()["url"] == expected_url, \
            'Expect url to be set. Got: {}' . format(response.json()["url"])

Notes:

  • Here we check that the correct information is returned. Watch the output in your terminal.
Clone this wiki locally