From ba16206131bc07091d7ee62d2c3f81688e1131bf Mon Sep 17 00:00:00 2001 From: Gabriel Borges <31298094+gabrielborgesdm@users.noreply.github.com> Date: Sun, 19 May 2024 16:38:48 -0300 Subject: [PATCH] SQlite for unit testing, depreciated methods replaced and unit tests for service layer (#2) * test: create base test class * feat: removed depreciated methods and fixed nested session issues * test: setup sqlite, tests for management_service --- backend/app/services/management_service.py | 13 ++-- backend/tests/conftest.py | 23 ++++++ .../tests/controllers/test_book_controller.py | 7 +- backend/tests/mocks/author_mock.py | 1 + backend/tests/mocks/books_mock.py | 14 +++- backend/tests/mocks/mock_utils.py | 9 +++ backend/tests/services/__init__.py | 0 .../tests/services/test_management_service.py | 71 +++++++++++++++++++ 8 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 backend/tests/mocks/mock_utils.py create mode 100644 backend/tests/services/__init__.py create mode 100644 backend/tests/services/test_management_service.py diff --git a/backend/app/services/management_service.py b/backend/app/services/management_service.py index 370a8d7..734847b 100644 --- a/backend/app/services/management_service.py +++ b/backend/app/services/management_service.py @@ -10,24 +10,23 @@ class ManagementService: def get_authors(self): - authors = AuthorModel.query.all() - + authors = db.session.query(AuthorModel).all() return authors_schema.dump(authors) def get_books(self): - books = BookModel.query.all() - + books = db.session.query(BookModel).all() return books_schema.dump(books) def create_author(self, author_data: Dict) -> AuthorSchema: - with db.session.begin(): + with db.session.begin(True): author = AuthorModel(**author_data) db.session.add(author) return author_schema.dump(author) def create_book(self, book_dto: Dict): - with db.session.begin(): + with db.session.begin(True): authors = self._get_authors_for_book_creation(book_dto.get("authors")) + del book_dto["authors"] book = BookModel(**book_dto) book.authors = authors @@ -47,7 +46,7 @@ def _find_or_create_author( if existent_id is None: return AuthorModel(**author_dto.get("authorCreationPayload")) - author = AuthorModel.query.get(existent_id) + author = db.session.get(AuthorModel, existent_id) if author is None: raise BadRequest(f"author of id {existent_id} was not found") diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 2aec0be..1f30597 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,3 +1,4 @@ +import unittest from flask import Flask from backend.app.handlers.http_error_handler import handle_exception @@ -13,6 +14,28 @@ app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db.init_app(app) + app.register_blueprint(books_bp) app.register_blueprint(authors_bp) app.register_error_handler(400, handle_exception) + + +class BaseTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.app = app + cls.app_context = cls.app.app_context() + cls.app_context.push() + cls.db = db + + @classmethod + def tearDownClass(cls): + cls.app_context.pop() + + def setUp(self): + self.db.session.close() + self.reset_db() + + def reset_db(self): + self.db.drop_all() + self.db.create_all() diff --git a/backend/tests/controllers/test_book_controller.py b/backend/tests/controllers/test_book_controller.py index 3e73c66..1d15bda 100644 --- a/backend/tests/controllers/test_book_controller.py +++ b/backend/tests/controllers/test_book_controller.py @@ -2,10 +2,7 @@ from unittest.mock import patch from backend.tests.conftest import app -from backend.tests.mocks.books_mock import ( - book_create_with_invalid_author_mock, - book_create_with_existent_author_id_mock, -) +from backend.tests.mocks.books_mock import book_create_with_invalid_author_mock, book_create_mock class TestGetBooks(unittest.TestCase): @@ -48,7 +45,7 @@ def test_should_succeed(self, mock_create_book): mock_create_book.return_value = books_mock_result with app.test_client() as client: - response = client.post("/books/", json=book_create_with_existent_author_id_mock) + response = client.post("/books/", json=book_create_mock) self.assertEqual(response.status_code, 201) diff --git a/backend/tests/mocks/author_mock.py b/backend/tests/mocks/author_mock.py index bf5c6e7..3fdc1cf 100644 --- a/backend/tests/mocks/author_mock.py +++ b/backend/tests/mocks/author_mock.py @@ -5,6 +5,7 @@ "birthDate": "2001-01-01", } + author_create_without_name_mock = { "email": "email@test.com", "nationality": "Brazil", diff --git a/backend/tests/mocks/books_mock.py b/backend/tests/mocks/books_mock.py index 6369233..97f31f1 100644 --- a/backend/tests/mocks/books_mock.py +++ b/backend/tests/mocks/books_mock.py @@ -1,11 +1,21 @@ +from backend.tests.mocks.author_mock import author_create_mock + book_create_with_invalid_author_mock = { "title": "Aboovk", "authors": [{"nonExistentProperty": "123"}], "pages": 100, } -book_create_with_existent_author_id_mock = { +book_create_mock = { "title": "Aboovk", - "authors": [{"existentAuthorId": "123"}], + "authors": [{"authorCreationPayload": author_create_mock}], "pages": 100, } + + +def get_book_with_authors(authors): + return { + "title": "Aboovk", + "authors": authors, + "pages": 100, + } diff --git a/backend/tests/mocks/mock_utils.py b/backend/tests/mocks/mock_utils.py new file mode 100644 index 0000000..8181e8a --- /dev/null +++ b/backend/tests/mocks/mock_utils.py @@ -0,0 +1,9 @@ +import copy + + +def get_mock_with_custom_args(mock_dict, **kwargs): + custom_mock = copy.deepcopy(mock_dict) + for key, value in kwargs.items(): + custom_mock[key] = value + + return custom_mock diff --git a/backend/tests/services/__init__.py b/backend/tests/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/tests/services/test_management_service.py b/backend/tests/services/test_management_service.py new file mode 100644 index 0000000..7c4af85 --- /dev/null +++ b/backend/tests/services/test_management_service.py @@ -0,0 +1,71 @@ +from datetime import date +from backend.tests.conftest import BaseTestCase +from backend.tests.mocks.author_mock import author_create_mock, author_create_without_name_mock +from backend.tests.mocks.mock_utils import get_mock_with_custom_args +from backend.tests.mocks.books_mock import ( + book_create_with_invalid_author_mock, + get_book_with_authors, +) + +from backend.app.services.management_service import ManagementService + +management_service = ManagementService() + +author_mock = get_mock_with_custom_args(author_create_mock, birthDate=date.today()) +book_mock = get_book_with_authors([{"authorCreationPayload": author_mock}]) + + +class TestCreateBook(BaseTestCase): + def test_should_create_book_and_its_author(self): + self.assertEqual(len(management_service.get_authors()), 0) + + book = management_service.create_book(book_mock) + + self.assertEqual(len(management_service.get_authors()), 1) + + self.assertEqual(book.get("title"), book_mock.get("title")) + self.assertEqual(book.get("pages"), book_mock.get("pages")) + + def test_should_create_book_and_find_a_created_author(self): + self.assertEqual(len(management_service.get_authors()), 0) + + author = management_service.create_author(author_mock) + + self.assertIsNotNone(author.get("id")) + book = management_service.create_book( + get_book_with_authors([{"existentAuthorId": author.get("id")}]) + ) + + self.assertEqual(len(management_service.get_authors()), 1) + + self.assertEqual(book.get("title"), book_mock.get("title")) + self.assertEqual(book.get("pages"), book_mock.get("pages")) + + def test_should_fail_when_book_does_not_have_required_field(self): + with self.assertRaises(Exception): + management_service.create_book(book_create_with_invalid_author_mock) + + +class TestCreateAuthor(BaseTestCase): + def test_should_create_author(self): + author = management_service.create_author(author_mock) + self.assertEqual(author.get("name"), author_mock.get("name")) + self.assertEqual(author.get("email"), author_mock.get("email")) + + def test_should_fail_when_author_does_not_have_required_field(self): + with self.assertRaises(Exception): + management_service.create_author(author_create_without_name_mock) + + +class TestGetAuthors(BaseTestCase): + def test_count_should_start_being_zero(self): + authors = management_service.get_authors() + self.assertEqual(len(authors), 0) + + def test_should_have_one_author_after_creation(self): + + author = management_service.create_author(author_mock) + + authors = management_service.get_authors() + self.assertEqual(len(authors), 1) + self.assertEqual(author.get("name"), authors[0].get("name"))