Skip to content
Snippets Groups Projects
Commit 229b01e3 authored by florimondmanca's avatar florimondmanca
Browse files

improve tests, set read-only fields

parent f3771c66
No related branches found
No related tags found
No related merge requests found
......@@ -54,6 +54,7 @@ class UserFactory(factory.DjangoModelFactory):
before_today=True, after_today=False,
locale='fr_FR')
phone_number = factory.Faker('phone_number', locale='fr_FR')
gender = factory.Iterator([User.MALE, User.FEMALE])
@classmethod
def _create(cls, model_class, *args, **kwargs):
......
"""School API tests."""
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase
from users.models import Student
from tutoring.models import School
from tests.utils import random_email, random_uai_code, ModelAPITestCase
from tests.factory import SchoolFactory, UserFactory
from tests.utils import AuthAPITestMixin
from tests.utils import APIReadTestMixin
from tests.utils import APIPostRequestTestMixin
User = get_user_model()
class SchoolAPITest(ModelAPITestCase):
"""Test the school API."""
class SchoolReadTest(AuthAPITestMixin, APIReadTestMixin, APITestCase):
"""Test reading schools from API as authenticated user."""
model = School
factory = SchoolFactory
list_url = '/api/schools/'
retrieve_url_format = '/api/schools/{obj.pk}/'
data_content_keys = ('uai_code', 'students', 'name', 'url',
'students_count',)
def create_data(self):
data = {
'uai_code': random_uai_code(),
'name': 'Lycée Michelin',
}
return data
@classmethod
def get_user(cls):
return UserFactory.create()
def test_list(self):
n_items = 5
for _ in range(n_items):
self.create_obj()
url = '/api/schools/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), n_items)
def test_retrieve(self):
obj = self.create_obj()
url = f'/api/schools/{obj.pk}/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
class SchoolCreateTest(AuthAPITestMixin, APIPostRequestTestMixin, APITestCase):
"""Test creating a school as an authenticated user."""
def test_data_has_expected_values(self):
obj = self.create_obj()
url = f'/api/schools/{obj.pk}/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
keys = (
'uai_code', 'students', 'name', 'url',
'students_count',
)
for key in keys:
self.assertIn(key, response.data)
url = '/api/schools/'
def test_create(self):
"""Ensure we can create a new object through the API."""
data = self.create_data()
names = (('John', 'Doe'), ('Alice', 'Smith'), ('Adam', 'Adam'))
for first_name, last_name in names:
user = User.objects.create(email=random_email(),
first_name=first_name,
last_name=last_name)
Student.objects.create(user=user)
data['students'] = Student.objects.all()
students_serialized = [student.get_absolute_url()
for student in data['students']]
data_serialized = {**data, 'students': students_serialized}
@classmethod
def get_user(cls):
return UserFactory.create()
url = '/api/schools/'
response = self.client.post(url, data_serialized, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def get_obj(self):
return SchoolFactory.build()
self.assertEqual(School.objects.count(), 1)
school_students = School.objects.get().students.all()
# print(school_students)
# print(data['students'])
self.assertQuerysetEqual(school_students, map(repr, data['students']),
ordered=False)
self.assertEqual(School.objects.get().name, 'Lycée Michelin')
def get_post_data(self, obj):
return {
'name': obj.name,
'uai_code': obj.uai_code,
'address': obj.address,
}
"""Student API tests."""
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase
from users.models import Student
from tutoring.models import School, TutoringGroup
from tests.factory import (
StudentFactory, UserFactory, SchoolFactory, TutoringGroupFactory)
from tests.utils import AuthAPITestMixin
from tests.utils import APIReadTestMixin
from tests.utils import APIPostRequestTestMixin
from tests.utils import random_email, random_uai_code, ModelAPITestCase
User = get_user_model()
class StudentAPITest(ModelAPITestCase):
"""Test the student API."""
class StudentReadTest(AuthAPITestMixin, APIReadTestMixin, APITestCase):
"""Test read students as authenticated user."""
model = Student
factory = StudentFactory
list_url = '/api/students/'
retrieve_url_format = '/api/students/{obj.pk}/'
data_content_keys = ('user', 'address', 'tutoring_group', 'school', 'url')
def create_data(self):
user = User.objects.create(email=random_email())
school = School.objects.create(uai_code=random_uai_code())
tutoring_group = TutoringGroup.objects.create()
data = {
'user': user,
'address': '3 Place de la Barre, 59000 LILLE',
'school': school,
'tutoring_group': tutoring_group,
}
return data
@classmethod
def get_user(cls):
return UserFactory.create()
def test_list(self):
n_items = 5
for _ in range(n_items):
self.create_obj()
url = '/api/students/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), n_items)
def test_retrieve(self):
student = self.create_obj()
url = f'/api/students/{student.pk}/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
class StudentCreateTest(APIPostRequestTestMixin, APITestCase):
"""Test create student as anonymous user."""
def test_data_has_expected_values(self):
student = self.create_obj()
url = f'/api/students/{student.pk}/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
keys = (
'user', 'address', 'tutoring_group', 'school', 'url',
)
for key in keys:
self.assertIn(key, response.data)
def test_create(self):
"""Ensure we can create a new student object through the API."""
data = self.create_data()
hyperlinked = 'user school tutoring_group'.split()
data_serialized = {
key: key in hyperlinked and value.get_absolute_url() or value
for key, value in data.items()
}
url = '/api/students/'
response = self.client.post(url, data_serialized, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Student.objects.count(), 1)
self.assertEqual(Student.objects.get().user, data.get('user'))
self.assertEqual(Student.objects.get().school, data.get('school'))
self.assertEqual(Student.objects.get().tutoring_group,
data.get('tutoring_group'))
def get_obj(self):
user = UserFactory.create()
school = SchoolFactory.create()
tutoring_group = TutoringGroupFactory.create(school=school)
obj = StudentFactory.build(user=user, school=school,
tutoring_group=tutoring_group)
return obj
def get_post_data(self, obj):
return {
'user': obj.user.get_absolute_url(),
'address': obj.address,
'school': obj.school.get_absolute_url(),
'tutoring_group': obj.tutoring_group.get_absolute_url(),
}
"""Tutor API tests."""
from rest_framework import status
from rest_framework.test import APITestCase
from tests.utils import AuthModelAPITestCase, ModelAPITestCase
from users.models import Tutor
from tests.utils import AuthAPITestMixin
from tests.utils import APIReadTestMixin
from tests.utils import APIPostRequestTestMixin
from tests.factory import TutorFactory, UserFactory
class TutorAPIAsStandardUser(AuthModelAPITestCase):
"""Test the tutors API for standard users."""
class TutorReadTest(AuthAPITestMixin, APIReadTestMixin,
APITestCase):
"""Test read tutors from API as authenticated user."""
model = Tutor
factory = TutorFactory
list_url = '/api/tutors/'
retrieve_url = '/api/tutors/{obj.pk}/'
data_content_keys = ('user', 'promotion', 'tutoring_groups', 'url',)
@classmethod
def get_user(cls):
return UserFactory.create()
def test_list(self):
n_items = 5
for _ in range(n_items):
TutorFactory.create()
class TutorCreateTest(APIPostRequestTestMixin, APITestCase):
"""Test create tutor as anonymous user."""
url = '/api/tutors/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), n_items)
def test_retrieve(self):
obj = TutorFactory.create()
url = obj.get_absolute_url()
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_data_has_expected_values(self):
obj = TutorFactory.create()
url = obj.get_absolute_url()
response = self.client.get(url)
keys = (
'user', 'promotion', 'tutoring_groups', 'url',
)
for key in keys:
self.assertIn(key, response.data)
class TutorCreateAPITest(ModelAPITestCase):
"""Test creation of tutors as un-authenticated user."""
def test_can_create(self):
"""Ensure we can create a new tutor through API."""
def get_obj(self):
user = UserFactory.create()
obj = TutorFactory.build(user=user)
data = {
'user': user.get_absolute_url(),
return obj
def get_post_data(self, obj):
return {
'user': obj.user.get_absolute_url(),
'promotion': obj.promotion,
'tutoring_groups': [],
}
url = '/api/tutors/'
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
"""School API tests."""
from rest_framework.test import APITestCase
from rest_framework import status
from tutoring.models import TutoringGroup
from tests.utils import AuthModelAPITestCase
from tests.utils import AuthAPITestMixin
from tests.utils import APIReadTestMixin
from tests.utils import APIPostRequestTestMixin
from tests.factory import (
TutoringGroupFactory, UserFactory, VpTutoratTutorFactory,
SchoolFactory,
TutoringGroupFactory, UserFactory, VpTutoratTutorFactory, SchoolFactory
)
class TutoringGroupAPIAsStandardUser(AuthModelAPITestCase):
"""Test the tutoring group API for standard users."""
class TutoringGroupReadTest(AuthAPITestMixin, APIReadTestMixin,
APITestCase):
"""Test authenticated user can read tutoring groups."""
model = TutoringGroup
factory = TutoringGroupFactory
list_url = '/api/tutoring/groups/'
retrieve_url_format = '/api/tutoring/groups/{obj.pk}/'
data_content_keys = ('id', 'url', 'name', 'tutors', 'students',
'tutors_count', 'students_count', 'school',)
def get_obj(self):
return TutoringGroupFactory.create()
@classmethod
def get_user(cls):
return UserFactory.create()
def test_can_list(self):
n_items = 5
for _ in range(n_items):
TutoringGroupFactory.create()
class CreateTutoringGroupStandardUser(AuthAPITestMixin,
APIPostRequestTestMixin,
APITestCase):
"""Test a standard user cannot create a tutoring group."""
url = '/api/tutoring/groups/'
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), n_items)
def test_can_retrieve(self):
obj = TutoringGroupFactory.create()
response = self.client.get(obj.get_absolute_url())
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_data_has_expected_values(self):
obj = TutoringGroupFactory.create()
response = self.client.get(obj.get_absolute_url())
keys = (
'id', 'url', 'name', 'tutors', 'students',
'tutors_count', 'students_count',
'school',
)
for key in keys:
self.assertIn(key, response.data)
expected_status_code = status.HTTP_403_FORBIDDEN
def test_cannot_create(self):
obj = TutoringGroupFactory.create()
data = {
def get_obj(self):
school = SchoolFactory.create()
return TutoringGroupFactory.build(school=school)
def get_post_data(self, obj):
return {
'name': obj.name,
'school': obj.school.get_absolute_url(),
'tutors': obj.tutors.all(),
'students': obj.students.all(),
}
url = '/api/tutoring/groups/'
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@classmethod
def get_user(cls):
return UserFactory.create()
class TutoringGroupAPIAsVpTutorat(AuthModelAPITestCase):
"""Test the tutoring group API for VP Tutorat users."""
model = TutoringGroup
class CreateTutoringGroupVpTutorat(CreateTutoringGroupStandardUser):
"""Test a VP Tutorat user can create a tutoring group."""
expected_status_code = status.HTTP_201_CREATED
@classmethod
def get_user(cls):
tutor = VpTutoratTutorFactory.create()
return tutor.user
def test_can_create(self):
"""Ensure a VP Tutorat can create new tutoring group."""
school = SchoolFactory.create()
obj = TutoringGroupFactory.build(school=school)
data = {
'name': obj.name,
'school': obj.school.get_absolute_url(),
'tutors': [],
'students': [],
}
url = '/api/tutoring/groups/'
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
"""API test utilities."""
from rest_framework.test import APITestCase
from rest_framework import status
__all__ = ('ModelAPITestCase', 'AuthModelAPITestCase')
__all__ = ('ModelAPITestCase', 'AuthAPITestMixin', 'APIReadTestMixin',
'APIPostRequestTestMixin')
class ModelAPITestCase(APITestCase):
......@@ -27,8 +30,8 @@ class ModelAPITestCase(APITestCase):
return self.model.objects.create(**kwargs)
class AuthModelAPITestCase(ModelAPITestCase):
"""Generic model API test case with an authenticated user."""
class AuthAPITestMixin:
"""Mixin class to use a test case with a logged in user."""
@classmethod
def get_user(cls):
......@@ -41,3 +44,96 @@ class AuthModelAPITestCase(ModelAPITestCase):
def setUp(self):
self.client.force_login(self.user)
class APIReadTestMixin:
"""Test mixin suited for testing read actions (list, retrieve) on models.
Attributes
----------
model : django.db.models.Model
factory : factory.Factory
A FactoryBoy factory used to create a test object.
list_url : str
retrieve_url_format : str
Formatted string with an {obj} tag.
Example: 'api/students/{obj.pk}/'
n_items : int, optional
Number of items to generate in test_list().
data_content_keys : tuple
"""
model = None
factory = None
list_url = ''
retrieve_url_format = ''
n_items = 5
data_content_keys = ()
def test_list(self):
"""Test the list action.
Creates a list of objects and performs an HTTP GET at `list_url`.
Succeeds if request status code is 200.
"""
n_before = self.model.objects.all().count()
self.factory.create_batch(self.n_items)
url = self.list_url
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data) - n_before, self.n_items)
def test_retrieve(self):
"""Test the retrieve action.
Retrieves an object as in test_retrieve and checks that keys specified
in `data_content_keys` are contained in response.data.
"""
obj = self.factory.create()
url = self.retrieve_url_format.format(obj=obj)
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_data_content(self):
"""Test the content of a retrieve action data.
Retrieves an object as in test_retrieve and checks that keys specified
in `data_content_keys` are contained in response.data.
"""
obj = self.factory.create()
url = obj.get_absolute_url()
response = self.client.get(url)
keys = self.data_content_keys
for key in keys:
self.assertIn(key, response.data)
class APIRequestTestMixin:
"""Generic API request test mixin."""
url = ''
expected_status_code = None
def get_url(self):
return self.url
class APIPostRequestTestMixin(APIRequestTestMixin):
"""Generic API POST request test mixin."""
expected_status_code = status.HTTP_201_CREATED
def get_obj(self):
"""Return a test object to extract POST data from."""
raise NotImplementedError
def get_post_data(self, obj):
"""Return data to send in POST request."""
raise NotImplementedError
def test_post(self):
"""Perform the POST request test."""
obj = self.get_obj()
data = self.get_post_data(obj)
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, self.expected_status_code)
......@@ -3,8 +3,8 @@
from rest_framework import serializers
from .models import (
TutoringGroup, School, TutoringSession, TutorTutoringGroup)
from users.models import Student, Tutor
TutoringGroup, School, TutoringSession)
from users.models import Student
class SchoolSerializer(serializers.HyperlinkedModelSerializer):
......@@ -70,12 +70,12 @@ class TutoringGroupSerializer(serializers.HyperlinkedModelSerializer):
tutors = serializers.HyperlinkedRelatedField(
many=True,
queryset=Tutor.objects.all(),
read_only=True,
view_name='api:tutor-detail',
)
students = serializers.HyperlinkedRelatedField(
many=True,
queryset=Student.objects.all(),
read_only=True,
view_name='api:student-detail',
)
school = serializers.HyperlinkedRelatedField(
......@@ -87,22 +87,6 @@ class TutoringGroupSerializer(serializers.HyperlinkedModelSerializer):
tutors_count = serializers.IntegerField(source='tutors.count',
read_only=True)
def create(self, validated_data):
tutors = validated_data.pop('tutors')
students = validated_data.pop('students')
is_leader = validated_data.pop('is_leader', False)
tutoring_group = TutoringGroup.objects.create(**validated_data)
for student in students:
tutoring_group.students.add(student)
for tutor in tutors:
TutorTutoringGroup.objects.create(
tutoring_group=tutoring_group,
tutor=tutor,
is_leader=is_leader,
)
tutoring_group.save()
return tutoring_group
class Meta: # noqa
model = TutoringGroup
fields = ('id', 'url', 'name', 'tutors', 'students', 'school',
......
......@@ -80,7 +80,7 @@ class ProfileSerializer(serializers.Serializer):
"""Base serializer for profile models."""
user = serializers.HyperlinkedRelatedField(
queryset=get_user_model().objects.all(),
read_only=True,
view_name='api:user-detail',
)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment