-
Notifications
You must be signed in to change notification settings - Fork 13
Assignment 5
- Student has Github account
- Student has Docker installed.
- Adequate bandwidth. This is not a cafe exercise.
- Student has completed Assignment 3
- 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.
git clone https://github.com/hackoregon/hacku-devops-2017.git
Go to the assignment directory
cd hacku-devops-2017/assign5
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.
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 TestClient
which we setup insetUp()
. 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.
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.