Building a carpool web API with Django (part 7)
Happy New Year! I hope you had a wonderful time during the December holidays. Myself I had so much fun at my rural home in Zimbabwe where the we were celebrating my grandfather’s 80th birthday. I also experienced the effects of climate change first hand. There was a severe shortage of drinking water both for humans and livestock which forces everyday life to revolve around looking for water, digging wells and rescuing animals that fell into muddy pools. Now that the holidays are over we can continue working on our REST API.
In part 6 of our series we added filtering support to our trips
endpoint to allow users to filter trips by origin, destination, number of seats and date. In this post we are going to talk about writing unit tests for our REST API. This is going to be a very short read as most of the things are repetitive. We are going to work with the trips
endpoint. For more in-depth information on writing tests with the Django REST Framework visit this link.
TripApiTest
Inside trips/tests.py
add the following:
# trips/tests.py
# ...
from rest_framework import status
from rest_framework.test import APITestCase
# ...
def create_data():
user = User.objects.create_user(
username='vince',
email='vince@test.com',
password='testpass123'
)
vehicle = Vehicle(
make='Make',
model='Model',
reg_number='1234',
user=user
)
vehicle.save()
origin = Place(name='Origin')
origin.save()
destination = Place(name='Destination')
destination.save()
Test Get Trips
Let’s create our TripApiTest
class that inherits from APITestCase
and add a test for getting trips:
# trips/tests.py
class TripApiTest(APITestCase):
def test_get_trips(self):
create_data()
user = User.objects.get(pk=1)
vehicle = Vehicle.objects.get(pk=1)
origin = Place.objects.get(pk=1)
destination = Place.objects.get(pk=2)
trip = Trip(
user=user,
origin=origin,
destination=destination,
vehicle=vehicle,
trip_date=date.today()
)
trip.save()
response = self.client.get('/api/v1/trips/')
self.assertEqual(json.loads(response.content), [
{
"id": 1,
"url": "http://testserver/api/v1/trips/1/",
"trip_date": str(date.today()),
"num_seats": 1,
"origin": {
"id": 1,
"name": "Origin"
},
"destination": {
"id": 2,
"name": "Destination"
},
"vehicle": {
"id": 1,
"url": "http://testserver/api/v1/vehicles/1/",
"make": "Make",
"model": "Model",
"reg_number": "1234",
"image": None,
"owner_url": "http://testserver/api/v1/users/1/"
},
"driver": {
"username": "vince",
"email": "vince@test.com",
"first_name": "",
"last_name": "",
"gender": "wont-say",
"birth_date": None,
"url": "http://testserver/api/v1/users/1/",
"profile_pic": None
}
}
])
The APITestCase
class provide a self.client
attribute which is an instance of APIClient
. It gives us the ability to easily call REST methods – get
, post
etc.
Adding authentication to tests
The APIClient
class provides many ways of authenticating your requests. Below are the different ways of authentication and when they are useful.
.login
The APIClient
class provides a login
method. It allows you to authenticate requests against any views which include SessionAuthentication
. This method is appropriate for testing APIs that use session authentication, for instance websites which include AJAX interaction with the API. Here’s an example of how you can use the login
method:
client = APIClient()
client.login(username='vince', password='testpass1234')
# log out
client.logout()
.credentials
The credentials
method is used to set headers that will be included in all requests by the test client. This method is appropriate for testing APIs that require authentication headers, such as basic authentication, OAuth and simple token authentication schemes. Here is an example of how you can use the credentials
method:
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient
token = Token.objects.get(user__username='vince')
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
.force_authenticate
This method is used when you want to bypass authentication entirely and force all requests to be automatically treated as authenticated. This is a very useful shortcut if you don’t want to construct valid authentication credentials. We are going to use this method when we test adding a trip.
Inside the TripApiTest
class add a test for adding a new trip:
def test_create_trip(self):
create_data()
user = User.objects.get(pk=1)
self.client.force_authenticate(user=user) # force authentication
response = self.client.post('/api/v1/trips/', {
'trip_date': str(date.today()),
'origin_id': 1,
'destination_id': 2,
'vehicle_id': 1
})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
trip = Trip.objects.get(pk=1)
self.assertEqual(trip.origin.name, 'Origin')
self.assertEqual(trip.destination.name, 'Destination')
self.assertEqual(trip.trip_date, date.today())
self.assertEqual(trip.user, user)
I am not going to add all the tests here. I just wanted to show you how you can write unit tests for your REST API. If you want to see all the tests check them out on GitHub. Many thanks for taking your time to read and once again, Happy New Year! I wish you a happy and prosperous 2020.
Comments