Skip to content

Commit

Permalink
Introduce RetrieveAPIAutoTestCase
Browse files Browse the repository at this point in the history
This introduces the first auto test case for the retrieve API. The implementor provides the actual object under test, and a tuple of (username, password, expected_error). The expected error is what that user should get from the API. If it's `None`, that means the request should be accepted.

If the request is accepted, we then use the serializer to figure out what fields the object is going to have. Most of the time, it's pretty straightforward, but sometimes (like for decimal fields, or relational fields), we have to do some transformations.

This will (hopefully) make it much easier to add test cases to the API. Or, at the very least, it'll allow us to clean up our fugly existing test cases.

Task: #475
  • Loading branch information
kmeht committed Jul 2, 2016
1 parent 57384d0 commit c565186
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 102 deletions.
69 changes: 69 additions & 0 deletions huxley/api/tests/auto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright (c) 2011-2016 Berkeley Model United Nations. All rights reserved.
# Use of this source code is governed by a BSD License (see LICENSE).

from django.db import models
from rest_framework import serializers

from huxley.api.tests import RetrieveAPITestCase


class RetrieveAPIAutoTestCase(RetrieveAPITestCase):
NOT_AUTHENTICATED = 'not_authenticated'

@classmethod
def get_test_object(cls):
raise NotImplementedError('You must provide a test object to retrieve.')

@classmethod
def get_users(cls, obj):
raise NotImplementedError('You must provide test users.')

@classmethod
def setUpTestData(cls):
cls.object = cls.get_test_object()
cls.users = cls.get_users(cls.object)

def test(self):
for user_data in self.users:
username, password, expected_error = user_data
if username and password:
self.client.login(username=username, password=password)
response = self.get_response(self.object.id)

if expected_error == self.NOT_AUTHENTICATED:
self.assertNotAuthenticated(response)
else:
self.assert_response(response)

def assert_response(self, response):
serializer = self.view.serializer_class
expected_response = get_expected_response(
serializer,
serializer.Meta.model,
self.object,
)
self.assertEqual(response.data, expected_response)


def get_expected_response(serializer, model, test_object):
serializer_fields = serializer._declared_fields
expected = {}
for field_name in serializer.Meta.fields:
field = model._meta.get_field(field_name)
attr = getattr(test_object, field_name)

if isinstance(field, models.DecimalField):
attr = float(attr)

serializer_field = serializer_fields.get(field_name, None)
if serializer_field:
if isinstance(serializer_field, serializers.DateTimeField):
attr = attr.isoformat()
elif isinstance(serializer_field, serializers.ListField):
attr = list(getattr(test_object, serializer_field.source))
elif isinstance(serializer_field, serializers.ManyRelatedField):
attr = list(attr.all())

expected[field_name] = attr

return expected
118 changes: 16 additions & 102 deletions huxley/api/tests/school/test_school.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,116 +2,30 @@
# Use of this source code is governed by a BSD License (see LICENSE).

from huxley.api.tests import (DestroyAPITestCase, ListAPITestCase,
PartialUpdateAPITestCase, RetrieveAPITestCase)
PartialUpdateAPITestCase)
from huxley.api.tests.auto import RetrieveAPIAutoTestCase
from huxley.core.models import School
from huxley.utils.test import TestSchools, TestUsers

from huxley.api.views.school import SchoolDetail

class SchoolDetailGetTestCase(RetrieveAPITestCase):
url_name = 'api:school_detail'

def test_anonymous_user(self):
'''It should reject request from an anonymous user.'''
school = TestSchools.new_school()
response = self.get_response(school.id)

self.assertNotAuthenticated(response)

def test_self(self):
'''It should allow the get request from the user.'''
school = TestSchools.new_school()

self.client.login(username=school.advisor.username, password='test')
response = self.get_response(school.id)
self.assertEqual(response.data, {
'id': school.id,
'registered': school.registered.isoformat(),
'name': school.name,
'address': school.address,
'city': school.city,
'state': school.state,
'zip_code': school.zip_code,
'country': school.country,
'primary_name': school.primary_name,
'primary_gender': school.primary_gender,
'primary_email': school.primary_email,
'primary_phone': school.primary_phone,
'primary_type': school.primary_type,
'secondary_name': school.secondary_name,
'secondary_gender': school.secondary_gender,
'secondary_email': school.secondary_email,
'secondary_phone': school.secondary_phone,
'secondary_type': school.secondary_type,
'program_type': school.program_type,
'times_attended': school.times_attended,
'international': school.international,
'waitlist': school.waitlist,
'beginner_delegates':school.beginner_delegates,
'intermediate_delegates': school.intermediate_delegates,
'advanced_delegates': school.advanced_delegates,
'spanish_speaking_delegates': school.spanish_speaking_delegates,
'chinese_speaking_delegates': school.chinese_speaking_delegates,
'countrypreferences': school.country_preference_ids,
'committeepreferences': list(school.committeepreferences.all()),
'registration_comments': school.registration_comments,
'fees_owed': float(school.fees_owed),
'fees_paid': float(school.fees_paid),
'assignments_finalized': school.assignments_finalized,
})

def test_other_user(self):
'''it should not allow a get request from another user.'''
school = TestSchools.new_school()
TestUsers.new_user(username='user2', password='user2')
class SchoolDetailGetTestCase(RetrieveAPIAutoTestCase):
url_name = 'api:school_detail'
view = SchoolDetail

self.client.login(username='user2', password='user2')
response = self.get_response(school.id)
@classmethod
def get_test_object(cls):
return TestSchools.new_school()

self.assertPermissionDenied(response)

def test_superuser(self):
'''it should allow a get request from a superuser.'''
school = TestSchools.new_school()
@classmethod
def get_users(cls, test_object):
TestUsers.new_superuser(username='user1', password='user1')

self.client.login(username='user1', password='user1')
response = self.get_response(school.id)

self.assertEqual(response.data, {
'id': school.id,
'registered': school.registered.isoformat(),
'name': school.name,
'address': school.address,
'city': school.city,
'state': school.state,
'zip_code': school.zip_code,
'country': school.country,
'primary_name': school.primary_name,
'primary_gender': school.primary_gender,
'primary_email': school.primary_email,
'primary_phone': school.primary_phone,
'primary_type': school.primary_type,
'secondary_name': school.secondary_name,
'secondary_gender': school.secondary_gender,
'secondary_email': school.secondary_email,
'secondary_phone': school.secondary_phone,
'secondary_type': school.secondary_type,
'program_type': school.program_type,
'times_attended': school.times_attended,
'international': school.international,
'waitlist': school.waitlist,
'beginner_delegates': school.beginner_delegates,
'intermediate_delegates': school.intermediate_delegates,
'advanced_delegates': school.advanced_delegates,
'spanish_speaking_delegates': school.spanish_speaking_delegates,
'chinese_speaking_delegates': school.chinese_speaking_delegates,
'countrypreferences': school.country_preference_ids,
'committeepreferences': list(school.committeepreferences.all()),
'registration_comments': school.registration_comments,
'fees_owed': float(school.fees_owed),
'fees_paid': float(school.fees_paid),
'assignments_finalized': school.assignments_finalized,
})
return (
(None, None, cls.NOT_AUTHENTICATED),
(test_object.advisor.username, 'test', None),
('user1', 'user1', None),
)


class SchoolDetailPatchTestCase(PartialUpdateAPITestCase):
Expand Down

0 comments on commit c565186

Please sign in to comment.