From dbcf245a62ceff500bb309afb7fb96af886ce031 Mon Sep 17 00:00:00 2001 From: taptorestart Date: Sat, 30 Dec 2023 10:51:53 +0900 Subject: [PATCH] TST: Add bdd test --- backend/pytest.ini | 2 +- backend/requirements-test.txt | 1 + backend/requirements.txt | 2 +- backend/tests/apis/conftest.py | 160 ++++++++++++++++++ .../tests/apis/v1/forms/form_create.feature | 31 ++++ .../tests/apis/v1/forms/form_delete.feature | 31 ++++ backend/tests/apis/v1/forms/form_list.feature | 33 ++++ backend/tests/apis/v1/forms/form_test.py | 41 +++++ .../tests/apis/v1/forms/form_update.feature | 41 +++++ 9 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 backend/tests/apis/v1/forms/form_create.feature create mode 100644 backend/tests/apis/v1/forms/form_delete.feature create mode 100644 backend/tests/apis/v1/forms/form_list.feature create mode 100644 backend/tests/apis/v1/forms/form_test.py create mode 100644 backend/tests/apis/v1/forms/form_update.feature diff --git a/backend/pytest.ini b/backend/pytest.ini index 1b29bcc..a505a70 100644 --- a/backend/pytest.ini +++ b/backend/pytest.ini @@ -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 diff --git a/backend/requirements-test.txt b/backend/requirements-test.txt index 19d4a8e..14808f7 100644 --- a/backend/requirements-test.txt +++ b/backend/requirements-test.txt @@ -1,3 +1,4 @@ pytest==7.3.1 pytest-django==4.5.2 factory-boy==3.2.1 +pytest-bdd==7.0.1 diff --git a/backend/requirements.txt b/backend/requirements.txt index 32de4d9..c2e7169 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -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 diff --git a/backend/tests/apis/conftest.py b/backend/tests/apis/conftest.py index 803d073..1146619 100644 --- a/backend/tests/apis/conftest.py +++ b/backend/tests/apis/conftest.py @@ -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 @@ -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 diff --git a/backend/tests/apis/v1/forms/form_create.feature b/backend/tests/apis/v1/forms/form_create.feature new file mode 100644 index 0000000..5ba1341 --- /dev/null +++ b/backend/tests/apis/v1/forms/form_create.feature @@ -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. + 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 . + 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. diff --git a/backend/tests/apis/v1/forms/form_delete.feature b/backend/tests/apis/v1/forms/form_delete.feature new file mode 100644 index 0000000..a49c586 --- /dev/null +++ b/backend/tests/apis/v1/forms/form_delete.feature @@ -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. + 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 . + 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. \ No newline at end of file diff --git a/backend/tests/apis/v1/forms/form_list.feature b/backend/tests/apis/v1/forms/form_list.feature new file mode 100644 index 0000000..8269d3a --- /dev/null +++ b/backend/tests/apis/v1/forms/form_list.feature @@ -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. + 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 . + 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. diff --git a/backend/tests/apis/v1/forms/form_test.py b/backend/tests/apis/v1/forms/form_test.py new file mode 100644 index 0000000..4691baa --- /dev/null +++ b/backend/tests/apis/v1/forms/form_test.py @@ -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 diff --git a/backend/tests/apis/v1/forms/form_update.feature b/backend/tests/apis/v1/forms/form_update.feature new file mode 100644 index 0000000..279a467 --- /dev/null +++ b/backend/tests/apis/v1/forms/form_update.feature @@ -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. + 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 . + 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.