-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: adds testing to backend for availability (#116)
* chore: cleans up linting * fix: puts back deleted comment * refact: adds folders for testing structure * feat: sets up testing database * chore: notes needed documentation * chore: cleans up linting * fix: removes unnecessary test database * refact: changes view name to be more descriptive * test: converts view test file to end to end test for availability endpoint * test: adds availability model unit test * chore: adds init files to run tests correctly * test: adds test coverage and more comments * chore: moves file to correct folder and skips tests * chore: moves to models unit test folder * chore: cleans up linting * test: adds start after end time check * chore: cleans up linting * test: create init to run view tests * test: adds view unit tests * feat: adds validation for availability start and end times * test: all tests passing * chore: resolves merge conflicts * chore: resolving merge conflict * docs: adds basic documentation for testing * chore: cleans up linting * fix: puts back deleted comment * refact: adds folders for testing structure * feat: sets up testing database * chore: notes needed documentation * chore: cleans up linting * fix: removes unnecessary test database * refact: changes view name to be more descriptive * test: converts view test file to end to end test for availability endpoint * test: adds availability model unit test * chore: adds init files to run tests correctly * test: adds test coverage and more comments * chore: moves file to correct folder and skips tests * chore: moves to models unit test folder * chore: cleans up linting * test: adds start after end time check * chore: cleans up linting * test: create init to run view tests * test: adds view unit tests * feat: adds validation for availability start and end times * test: all tests passing * docs: adds basic documentation for testing * refact: reverting this to a try/catch exception setup * chore: cleans up merge * chore: cleans up linting * fix: puts back deleted comment * refact: adds folders for testing structure * feat: sets up testing database * chore: notes needed documentation * chore: cleans up linting * fix: removes unnecessary test database * test: converts view test file to end to end test for availability endpoint * test: adds availability model unit test * test: adds test coverage and more comments * chore: moves file to correct folder and skips tests * chore: moves to models unit test folder * chore: cleans up linting * test: adds view unit tests * feat: adds validation for availability start and end times * test: all tests passing * chore: resolves merge conflicts * chore: resolving merge conflict * docs: adds basic documentation for testing * chore: cleans up merge * fix: merge conflicts * fix: remove duplicate function * chore: cleans up linting * build: adds coverage.py and related files * fix: reverts to try/exception * chore: adds todo comments * docs: adds test coverage information * chore: cleans up linting * test: adds check for end time validation message * test: add str test for availability * fix: remove exemption details --------- Co-authored-by: GitLukeW <[email protected]>
- Loading branch information
Showing
17 changed files
with
626 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
[run] | ||
# Measure coverage for the 'team_production_system' directory only | ||
source = team_production_system | ||
|
||
# Omit test files and certain configuration files from coverage | ||
omit = | ||
team_production_system/tests/* | ||
team_production_system/apps.py | ||
team_production_system/manage.py | ||
team_production_system/__init__.py | ||
team_production_system/asgi.py | ||
team_production_system/wsgi.py | ||
team_production_system/admin.py | ||
team_production_system/urls.py | ||
team_production_system/migrations/* | ||
|
||
[report] | ||
# Exclude the same files from the coverage report | ||
omit = | ||
team_production_system/tests/* | ||
team_production_system/apps.py | ||
team_production_system/manage.py | ||
team_production_system/__init__.py | ||
team_production_system/asgi.py | ||
team_production_system/wsgi.py | ||
team_production_system/admin.py | ||
team_production_system/urls.py | ||
team_production_system/migrations/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,30 @@ [email protected] | |
|
||
3. Save the .env file. | ||
|
||
# Testing | ||
|
||
For testing this app, we are using [Django Test Case](https://docs.djangoproject.com/en/4.2/topics/testing/overview/) and [Django REST Framework API Test Case](https://www.django-rest-framework.org/api-guide/testing/#api-test-cases) along with [coverage.py](https://coverage.readthedocs.io/en/7.2.7/index.html) for test coverage reporting. | ||
|
||
To run tests: | ||
```python manage.py test``` | ||
|
||
To skip a test that isn't finished, add the following before the test class: | ||
```@unittest.skip("Test file is not ready yet")``` | ||
|
||
To run coverage for test: | ||
```coverage run manage.py test``` | ||
|
||
After you run tests you can get the report in command-line by running: | ||
```coverage report``` | ||
|
||
For an interactive html report, run: | ||
```coverage html``` | ||
|
||
Then in the `htmlcov` folder of the project, open the file `index.html` in a browser. Here you can see an indepth analysis of coverage and what lines need testing. Click available links to view specific file coverage data. | ||
|
||
Here is some helpful information on testing in Django and Django REST Framework: https://www.rootstrap.com/blog/testing-in-django-django-rest-basics-useful-tools-good-practices | ||
|
||
|
||
# API Reference | ||
|
||
API URL - https://team-production-system.onrender.com | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
167 changes: 167 additions & 0 deletions
167
team_production_system/tests/end_to_end_tests/test_availability_list_create.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
from django.urls import reverse | ||
from django.utils import timezone | ||
from rest_framework import status | ||
from rest_framework.test import APITestCase, APIClient | ||
from ...models import Availability, Mentor, CustomUser | ||
from ...serializers import AvailabilitySerializer | ||
|
||
|
||
class AvailabilityListCreateTestCase(APITestCase): | ||
def setUp(self): | ||
# Create a Mentor object | ||
self.user = CustomUser.objects.create_user( | ||
username='mentor', | ||
email='[email protected]', | ||
password='password' | ||
) | ||
self.mentor = Mentor.objects.create(user=self.user) | ||
|
||
# Create two Availability objects associated with the Mentor | ||
self.availability1 = Availability.objects.create( | ||
mentor=self.mentor, | ||
start_time=timezone.now(), | ||
end_time=timezone.now() + timezone.timedelta(hours=1) | ||
) | ||
self.availability2 = Availability.objects.create( | ||
mentor=self.mentor, | ||
start_time=timezone.now() + timezone.timedelta(days=1), | ||
end_time=timezone.now() + timezone.timedelta(days=1, hours=1) | ||
) | ||
# Create a Client | ||
self.client = APIClient() | ||
|
||
def test_get_availability_list_without_authentication(self): | ||
""" | ||
Test that a GET request to retrieve the list of Availabilities without | ||
authentication returns a status code of 401 UNAUTHORIZED. | ||
""" | ||
# Send a GET request to retrieve the list of Availabilities | ||
url = reverse('availability') | ||
response = self.client.get(url, format='json') | ||
|
||
# Check that the response has a status code of 401 UNAUTHORIZED | ||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) | ||
|
||
def test_get_availability_list(self): | ||
""" | ||
Test that a GET request to retrieve the list of Availabilities returns | ||
a status code of 200 OK and the correct serialized data. | ||
""" | ||
|
||
# Authenticate as the Mentor | ||
self.client.force_authenticate(user=self.user) | ||
|
||
# Send a GET request to retrieve the list of Availabilities | ||
url = reverse('availability') | ||
response = self.client.get(url, format='json') | ||
|
||
# Check that the response has a status code of 200 OK | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
# Check that the response data matches the serialized Availability obj | ||
availabilities = Availability.objects.filter( | ||
mentor=self.mentor, | ||
end_time__gte=timezone.now() | ||
).select_related('mentor__user') | ||
serializer = AvailabilitySerializer(availabilities, many=True) | ||
self.assertEqual(response.data, serializer.data) | ||
self.assertEqual(len(response.data), 2) | ||
|
||
def test_create_availability(self): | ||
""" | ||
Test that a POST request to create a new Availability with valid data | ||
returns a status code of 201 CREATED. | ||
""" | ||
|
||
# Authenticate as the Mentor | ||
self.client.force_authenticate(user=self.user) | ||
|
||
# Send a POST request to create a new Availability | ||
data = { | ||
'mentor': self.mentor.pk, | ||
'start_time': timezone.now() + timezone.timedelta(days=2), | ||
'end_time': timezone.now() + timezone.timedelta(days=2, hours=1) | ||
} | ||
url = reverse('availability') | ||
response = self.client.post(url, data, format='json') | ||
|
||
# Check that the response has a status code of 201 CREATED | ||
self.assertEqual(response.status_code, status.HTTP_201_CREATED) | ||
|
||
# Check that the created Availability object has the correct attributes | ||
availability = Availability.objects.last() | ||
self.assertEqual(availability.start_time, data['start_time']) | ||
self.assertEqual(availability.end_time, data['end_time']) | ||
self.assertEqual(availability.mentor, self.mentor) | ||
|
||
def test_create_availability_with_duplicate_start_time(self): | ||
""" | ||
Test that a POST request to create a new Availability with a start time | ||
that has already been used returns a status code of 400 BAD REQUEST | ||
and an error message. | ||
""" | ||
# Authenticate as the Mentor | ||
self.client.force_authenticate(user=self.user) | ||
|
||
'''Send a POST request to create a new Availability with | ||
a start time that has already been used''' | ||
data = { | ||
'mentor': self.mentor.pk, | ||
'start_time': self.availability1.start_time, | ||
'end_time': timezone.now() + timezone.timedelta(hours=2) | ||
} | ||
url = reverse('availability') | ||
response = self.client.post(url, data, format='json') | ||
|
||
# Check that the response has a status code of 400 BAD REQUEST | ||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
|
||
# Check that the response data contains an error message | ||
self.assertEqual(response.data[0], | ||
'Input overlaps with existing availability.') | ||
|
||
def test_create_availability_with_duplicate_start_and_end_times(self): | ||
""" | ||
Test that a POST request to create a new Availability with a start time | ||
and end time that have already been used returns a status code of | ||
400 BAD REQUEST and an error message. | ||
""" | ||
# Authenticate as the Mentor | ||
self.client.force_authenticate(user=self.user) | ||
|
||
# Send a POST request to create a new Availability with duplicate | ||
# start and end times | ||
data = { | ||
'mentor': self.mentor.pk, | ||
'start_time': self.availability1.start_time, | ||
'end_time': self.availability1.end_time | ||
} | ||
url = reverse('availability') | ||
response = self.client.post(url, data, format='json') | ||
|
||
# Check that the response has a status code of 400 BAD REQUEST | ||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
|
||
# Check that the response data contains an error message | ||
self.assertEqual(response.data[0], | ||
'Input overlaps with existing availability.') | ||
|
||
def test_create_availability_with_end_time_before_start_time(self): | ||
""" | ||
Test that a POST request to create a new Availability with a start time | ||
after the end time returns a status code of 400 BAD REQUEST. | ||
""" | ||
# Authenticate as the Mentor | ||
self.client.force_authenticate(user=self.user) | ||
|
||
availability_data = { | ||
'start_time': '2022-01-01T14:00:00Z', | ||
'end_time': '2022-01-01T12:00:00Z', | ||
'mentor': self.mentor.pk | ||
} | ||
response = self.client.post('/availability/', | ||
availability_data, format='json') | ||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
self.assertEqual(str(response.data['end_time'][0]), | ||
'End time must be in the future.') | ||
self.assertEqual(Availability.objects.count(), 2) |
6 changes: 4 additions & 2 deletions
6
team_production_system/tests.py → ...duction_system/tests/test_availability.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
39 changes: 39 additions & 0 deletions
39
team_production_system/tests/unit_tests/models/test_availability_model.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from django.test import TestCase | ||
from datetime import datetime, timedelta, timezone | ||
from ....models import Availability, Mentor, CustomUser | ||
|
||
|
||
class AvailabilityTestCase(TestCase): | ||
def setUp(self): | ||
# Create a User and Mentor object | ||
self.user = CustomUser.objects.create_user( | ||
username='mentor', | ||
email='[email protected]', | ||
password='password' | ||
) | ||
self.mentor = Mentor.objects.create(user=self.user) | ||
|
||
def test_create_availability(self): | ||
# Set start and end times | ||
start_time = datetime.now(timezone.utc) | ||
end_time = start_time + timedelta(hours=1) | ||
|
||
# Create an Availability object | ||
availability = Availability( | ||
mentor=self.mentor, | ||
start_time=start_time, | ||
end_time=end_time) | ||
|
||
# Save an Availability object associated with the Mentor | ||
availability.save() | ||
|
||
# Retrieve the saved Availability object | ||
saved_availability = Availability.objects.first() | ||
|
||
# Check that the object was saved correctly | ||
self.assertEqual(saved_availability.start_time, start_time) | ||
self.assertEqual(saved_availability.end_time, end_time) | ||
self.assertEqual(saved_availability.mentor, self.mentor) | ||
self.assertEqual(str(saved_availability), f"{self.mentor} is available from {start_time} to {end_time}.") | ||
|
||
# TODO: Add test for self reference |
Empty file.
Oops, something went wrong.