Skip to content

Commit

Permalink
TST: Add bdd test
Browse files Browse the repository at this point in the history
  • Loading branch information
taptorestart committed Dec 30, 2023
1 parent 40b1a91 commit dbcf245
Show file tree
Hide file tree
Showing 9 changed files with 340 additions and 2 deletions.
2 changes: 1 addition & 1 deletion backend/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[pytest]
DJANGO_SETTINGS_MODULE = config.settings.api
python_files = test_*.py
python_files = test_*.py *_test.py
testpaths = 'tests'
addopts = --reuse-db --nomigrations -ra -q -p no:warnings
1 change: 1 addition & 0 deletions backend/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pytest==7.3.1
pytest-django==4.5.2
factory-boy==3.2.1
pytest-bdd==7.0.1
2 changes: 1 addition & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Django
Django==4.2
Django==4.2.5
djangorestframework==3.14.0
drf-nested-routers==0.93.4
drf-spectacular==0.26.2
Expand Down
160 changes: 160 additions & 0 deletions backend/tests/apis/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import importlib
import json
from types import NoneType

import pytest
from django.contrib.auth.models import User
from pytest_bdd import given, parsers, when, then
from rest_framework.test import APIClient

from tests.apis.factories import UserFactory
Expand All @@ -17,3 +22,158 @@ def client_staff():
client: APIClient = APIClient()
client.force_authenticate(user=user_staff)
return client


DATA_TYPES = {
"bool": bool,
"int": int,
"str": str,
"float": float,
"list": list,
"tuple": tuple,
"dict": dict,
"NoneType": NoneType,
}


@given(parsers.parse("I am a/an {user_type} user."), target_fixture="user")
def i_am_a_user_type_user(user_type):
if user_type == "anonymous":
return None
if user_type == "staff":
return UserFactory(is_staff=True, is_superuser=False)
if user_type == "superuser":
return UserFactory(is_staff=True, is_superuser=True)
return UserFactory(is_staff=False, is_superuser=False)


@given(parsers.parse("I am logging in."), target_fixture="client")
def i_am_logging_in(user):
client: APIClient = APIClient()
if user:
client.force_authenticate(user=user)
return client


@when(parsers.parse("I am making a request to the server using the {method} and {path}."), target_fixture="response")
def i_am_making_a_request_to_the_server_using_the_method_and_path(client, method, path):
response = None
if method in ["GET", "get"]:
response = client.get(path=path)
if method in ["POST", "post"]:
response = client.post(path=path)
if method in ["PUT", "put"]:
response = client.put(path=path)
if method in ["PATCH", "patch"]:
response = client.patch(path=path)
if method in ["DELETE", "delete"]:
response = client.delete(path=path)
return response


@given(
parsers.parse("The data to be sent is as follows.\n{payload}"),
target_fixture="data",
)
def the_data_to_be_sent_is_as_follows(payload):
return json.loads(payload)


@when(
parsers.parse("I am making a request to the server with data using the {method} and {path}."),
target_fixture="response",
)
def i_am_making_a_request_to_the_server_with_data_using_the_method_and_path(client, method, path, data):
response = None
if method in ["GET", "get"]:
response = client.get(path=path, data=data)
if method in ["POST", "post"]:
response = client.post(path=path, data=data)
if method in ["PUT", "put"]:
response = client.put(path=path, data=data)
if method in ["PATCH", "patch"]:
response = client.patch(path=path, data=data)
if method in ["DELETE", "delete"]:
response = client.delete(path=path, data=data)
return response


@given(
parsers.parse("I will save the following data using {module}'s {factory_class_name}.\n{data}"),
)
def i_will_save_the_following_data_using_modules_factory_class_name(module: str, factory_class_name: str, data):
module = importlib.import_module(module)
factory_class = getattr(module, factory_class_name)
factory_class(**json.loads(data))
return None


@then(parsers.parse("The response status code is {status_code:d}."))
def the_response_status_code_is_status_code(response, status_code):
assert response.status_code == status_code


@then(parsers.parse("The number of result in the response JSON is {number:d}."))
def the_number_of_result_in_the_response_json_is_number(response, number):
assert len(response.json()) == number


@then(parsers.parse("The {field} data in the response JSON is the same as {value}."))
def the_field_data_in_the_response_json_is_the_same_as_value(response, field, value):
assert str(response.json()[field]) == value


@then(parsers.parse("The {field} data in the response JSON is of type {data_type} and the same as {value}."))
def the_field_data_in_the_response_json_is_of_type_data_type_and_is_the_same_as_value(
response, field, data_type, value
):
data = response.json()[field]
assert isinstance(data, DATA_TYPES.get(data_type, str)) is True
assert str(data) == value


@then(
parsers.parse(
"The {field} data in the {index:d}st/nd/rd/th entry of the response JSON list is the same as {value}."
)
)
def the_field_data_in_the_indexstndrdth_entry_of_the_response_json_list_is_the_same_as_value(
response, field, index, value
):
assert str(response.json()[int(index) - 1][field]) == value


@then(
parsers.parse(
"The {field} data in the {index:d}st/nd/rd/th entry of the response JSON list is of type {data_type} and the same as {value}."
)
)
def the_field_data_in_the_indexstndrdth_entry_of_the_response_json_results_list_is_of_type_data_type_and_the_same_as_value(
response, field, index, value
):
assert str(response.json()[int(index) - 1][field]) == value


@then(parsers.parse("The existence of data with an ID of {pk:d} in the {model} model from {module} is {existance}."))
def the_existence_of_data_with_an_id_of_pk_in_the_model_model_from_module_is_existance(pk, model, module, existance):
module = importlib.import_module(module)
model_class = getattr(module, model)
obj = model_class.objects.filter(pk=pk).last()
assert str(bool(obj)) == existance


@then(
parsers.parse(
"The {field} data of the {model} model from {module} with an ID of {pk:d} is of type {data_type} and the same as {value}."
)
)
def the_field_data_of_the_model_model_from_module_with_an_id_of_pk_is_of_type_data_type_and_the_same_as_value(
field, model, module, pk, data_type, value
):
module = importlib.import_module(module)
model_class = getattr(module, model)
obj = model_class.objects.filter(pk=pk).last()
attr = getattr(obj, field)
assert bool(obj) is True
assert isinstance(attr, DATA_TYPES.get(data_type, str)) is True
assert str(attr) == value
31 changes: 31 additions & 0 deletions backend/tests/apis/v1/forms/form_create.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@django_db
Feature: Form Create Test
Background:
Given The data to be sent is as follows.
"""
{
"slug": "test",
"title": "test",
"start_date": "2023-12-01",
"end_date": "2023-12-31"
}
"""

Scenario Outline: Form Create Permission Test
Given I am a/an <user_type> user.
And I am logging in.
When I am making a request to the server with data using the POST and /v1/forms/.
Then The response status code is <status_code>.
Examples:
| user_type | status_code |
| anonymous | 403 |
| general | 403 |
| staff | 201 |

Scenario: Form Create Test
Given I am a/an staff user.
And I am logging in.
When I am making a request to the server with data using the POST and /v1/forms/.
Then The response status code is 201.
And The slug data in the response JSON is the same as test.
And The title data in the response JSON is of type str and the same as test.
31 changes: 31 additions & 0 deletions backend/tests/apis/v1/forms/form_delete.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@django_db
Feature: Form Delete Test
Background:
Given I will save the following data using backend.tests.apis.factories's FormFactory.
"""
{
"id": 1,
"slug": "test",
"title": "test",
"start_date": "2023-12-01",
"end_date": "2023-12-31"
}
"""

Scenario Outline: Form Delete Permission Test
Given I am a/an <user_type> user.
And I am logging in.
When I am making a request to the server using the DELETE and /v1/forms/test/.
Then The response status code is <status_code>.
Examples:
| user_type | status_code |
| anonymous | 403 |
| general | 403 |
| staff | 204 |

Scenario: Form Delete Test
Given I am a/an staff user.
And I am logging in.
When I am making a request to the server using the DELETE and /v1/forms/test/.
Then The response status code is 204.
And The existence of data with an ID of 1 in the Form model from apps.forms.models is False.
33 changes: 33 additions & 0 deletions backend/tests/apis/v1/forms/form_list.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@django_db
Feature: Form List Test
Background:
Given I will save the following data using backend.tests.apis.factories's FormFactory.
"""
{
"id": 1,
"slug": "test",
"title": "test",
"start_date": "2023-12-01",
"end_date": "2023-12-31"
}
"""

Scenario Outline: Form List Permission Test
Given I am a/an <user_type> user.
And I am logging in.
When I am making a request to the server using the GET and /v1/forms/.
Then The response status code is <status_code>.
Examples:
| user_type | status_code |
| anonymous | 200 |
| general | 200 |
| staff | 200 |

Scenario: Form List Test
Given I am a/an general user.
And I am logging in.
When I am making a request to the server using the GET and /v1/forms/.
Then The response status code is 200.
And The number of result in the response JSON is 1.
And The slug data in the 1st/nd/rd/th entry of the response JSON list is the same as test.
And The title data in the 1st/nd/rd/th entry of the response JSON list is of type str and the same as test.
41 changes: 41 additions & 0 deletions backend/tests/apis/v1/forms/form_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from pytest_bdd import scenario


@scenario("form_list.feature", "Form List Permission Test")
def test_form_list_permission_test():
pass


@scenario("form_list.feature", "Form List Test")
def test_form_list_test():
pass


@scenario("form_create.feature", "Form Create Permission Test")
def test_form_create_permission_test():
pass


@scenario("form_create.feature", "Form Create Test")
def test_form_create_test():
pass


@scenario("form_update.feature", "Form Partial Update Permission Test")
def test_form_partial_update_permission_test():
pass


@scenario("form_update.feature", "Form Partial Update Test")
def test_form_partial_update_test():
pass


@scenario("form_delete.feature", "Form Delete Permission Test")
def test_form_delete_permission_test():
pass


@scenario("form_delete.feature", "Form Delete Test")
def test_form_delete_test():
pass
41 changes: 41 additions & 0 deletions backend/tests/apis/v1/forms/form_update.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@django_db
Feature: Form Update Test
Background:
Given I will save the following data using backend.tests.apis.factories's FormFactory.
"""
{
"id": 1,
"slug": "test",
"title": "test",
"start_date": "2023-12-01",
"end_date": "2023-12-31"
}
"""
And The data to be sent is as follows.
"""
{
"title": "test1"
}
"""

Scenario Outline: Form Partial Update Permission Test
Given I am a/an <user_type> user.
And I am logging in.
When I am making a request to the server with data using the PATCH and /v1/forms/test/.
Then The response status code is <status_code>.
Examples:
| user_type | status_code |
| anonymous | 403 |
| general | 403 |
| staff | 200 |


Scenario: Form Partial Update Test
Given I am a/an staff user.
And I am logging in.
When I am making a request to the server with data using the PATCH and /v1/forms/test/.
Then The response status code is 200.
And The id data in the response JSON is the same as 1.
And The title data in the response JSON is the same as test1.
And The existence of data with an ID of 1 in the Form model from apps.forms.models is True.
And The title data of the Form model from apps.forms.models with an ID of 1 is of type str and the same as test1.

0 comments on commit dbcf245

Please sign in to comment.