From 9e909f15a1e4949408a1cee8a827edc2e9c95a33 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:29:24 +0400 Subject: [PATCH 01/17] docs(readme): document the test naming convention Adds a Testing strategy section, which the README did not have yet, with a Test naming convention subsection: every test method is named _NN_Should_When, so a failing test name alone tells what was expected and under which condition. --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index ec533c8..17d17c6 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ This document is the **complete specification** of the project: it is meant to b - [Branching strategy](#branching-strategy) - [Standard response format](#standard-response-format) - [Spring AOP](#spring-aop) +- [Testing strategy](#testing-strategy) + - [Test naming convention](#test-naming-convention) - [feature/core-architecture](#featurecore-architecture) - [feature/dao](#featuredao) - [feature/service](#featureservice) @@ -216,6 +218,30 @@ The project uses **Spring AOP** to illustrate aspect-oriented programming, kept - Also serves as a teaching base for the other advice types (`@Before`, `@After`, `@AfterReturning`, `@AfterThrowing`) alongside `@Around` - Illustrates the proxy-based nature of Spring AOP: since services are Spring-managed beans injected by interface (`CategoryService`, `ProductService`, ...), a JDK dynamic proxy is used rather than a CGLIB subclass proxy +## Testing strategy + +Every module ships its tests before its Pull Request is opened, with the tool that fits the module's place in the architecture. + +| Module | Tool | What it verifies | +|---|---|---| +| `dao` | JUnit 5 + `SpringExtension` + H2 | DAO methods against a real in-memory database, with the Spring context loaded from the module's configuration | +| `service` | JUnit 5 + Mockito, and `SpringExtension` for the context tests | Business rules with the DAO layer mocked, and the wiring of the service context | +| `web` | JUnit 5 + `MockMvc` | HTTP status codes, payload shape (`ApiResponse`) and error mapping, with the service layer mocked | + +### Test naming convention + +Every test method, in every module, is named `_NN_Should_When`: a two-digit, zero-padded sequence number (the order of the methods within the class, restarting at `_01_` in each class; JUnit does not enforce it, it is kept consistent by convention), followed by what is expected, followed by the condition that produces it. + +```java +@Test +void _01_ShouldReturnCategory_WhenCategoryExists() { ... } + +@Test +void _02_ShouldReturnEmpty_WhenCategoryDoesNotExist() { ... } +``` + +No other naming style (`shouldX()`, `testX()`, `givenX_whenY_thenZ()`, `savePersistsANewCategory()`) is used anywhere in this project's test suite. This applies to test methods only, not to `@BeforeEach`/`@AfterEach` helpers. + ## feature/core-architecture Technical foundation shared by the whole project, to be merged first into `develop`. From 44918628a4764ee69fd1b1890af3c6ad13a04c8e Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:29:39 +0400 Subject: [PATCH 02/17] test(dao): rename CategoryDaoTest methods to the _NN_Should_When convention Renames every test method of CategoryDaoTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../edgareldy/springtutorial/dao/CategoryDaoTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CategoryDaoTest.java b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CategoryDaoTest.java index 54317a1..c5d319e 100644 --- a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CategoryDaoTest.java +++ b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CategoryDaoTest.java @@ -35,7 +35,7 @@ class CategoryDaoTest { private CategoryDao categoryDao; @Test - void savePersistsANewCategory() { + void _01_ShouldPersistCategory_WhenCategoryIsNew() { Category category = new Category(); category.setCategoryName("Electronics"); @@ -45,14 +45,14 @@ void savePersistsANewCategory() { } @Test - void findByIdReturnsEmptyWhenCategoryDoesNotExist() { + void _02_ShouldReturnEmpty_WhenCategoryDoesNotExist() { Optional found = categoryDao.findById(-1L); assertFalse(found.isPresent()); } @Test - void findAllReturnsSavedCategories() { + void _03_ShouldReturnSavedCategories_WhenFindingAll() { Category category = new Category(); category.setCategoryName("Books"); categoryDao.save(category); @@ -63,7 +63,7 @@ void findAllReturnsSavedCategories() { } @Test - void countReflectsSavedCategories() { + void _04_ShouldReflectSavedCategories_WhenCounting() { long before = categoryDao.count(); Category category = new Category(); category.setCategoryName("Counted"); @@ -75,7 +75,7 @@ void countReflectsSavedCategories() { } @Test - void deleteByIdRemovesTheCategory() { + void _05_ShouldRemoveCategory_WhenCategoryIsDeletedById() { Category category = new Category(); category.setCategoryName("Temporary"); Category saved = categoryDao.save(category); From cea242c315f3c3773256d734346277792332511c Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:29:39 +0400 Subject: [PATCH 03/17] test(dao): rename CustomerDaoTest methods to the _NN_Should_When convention Renames every test method of CustomerDaoTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../edgareldy/springtutorial/dao/CustomerDaoTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CustomerDaoTest.java b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CustomerDaoTest.java index 0be17de..7b06aff 100644 --- a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CustomerDaoTest.java +++ b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/CustomerDaoTest.java @@ -35,7 +35,7 @@ class CustomerDaoTest { private CustomerDao customerDao; @Test - void savePersistsANewCustomer() { + void _01_ShouldPersistCustomer_WhenCustomerIsNew() { Customer customer = new Customer(); customer.setFirstName("Grace"); customer.setLastName("Hopper"); @@ -49,14 +49,14 @@ void savePersistsANewCustomer() { } @Test - void findByIdReturnsEmptyWhenCustomerDoesNotExist() { + void _02_ShouldReturnEmpty_WhenCustomerDoesNotExist() { Optional found = customerDao.findById(-1L); assertFalse(found.isPresent()); } @Test - void deleteByIdRemovesTheCustomer() { + void _03_ShouldRemoveCustomer_WhenCustomerIsDeletedById() { Customer customer = new Customer(); customer.setFirstName("Temp"); customer.setLastName("Customer"); @@ -71,7 +71,7 @@ void deleteByIdRemovesTheCustomer() { } @Test - void countReflectsSavedCustomers() { + void _04_ShouldReflectSavedCustomers_WhenCounting() { long before = customerDao.count(); Customer customer = new Customer(); customer.setFirstName("Counted"); @@ -87,7 +87,7 @@ void countReflectsSavedCustomers() { } @Test - void findAllReturnsSavedCustomers() { + void _05_ShouldReturnSavedCustomers_WhenFindingAll() { Customer customer = new Customer(); customer.setFirstName("Katherine"); customer.setLastName("Johnson"); From 2efba7de275bc8c3c2b7008f8d7ead2d9f0649e1 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:29:39 +0400 Subject: [PATCH 04/17] test(dao): rename OrderDaoTest methods to the _NN_Should_When convention Renames every test method of OrderDaoTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../edgareldy/springtutorial/dao/OrderDaoTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/OrderDaoTest.java b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/OrderDaoTest.java index aceeddb..ecdbb3a 100644 --- a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/OrderDaoTest.java +++ b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/OrderDaoTest.java @@ -71,7 +71,7 @@ void createCustomerAndProduct() { } @Test - void saveAndFindByIdResolveCustomerAndProduct() { + void _01_ShouldResolveCustomerAndProduct_WhenOrderIsSavedThenFoundById() { Order order = new Order(); order.setCustomer(customer); order.setProduct(product); @@ -86,7 +86,7 @@ void saveAndFindByIdResolveCustomerAndProduct() { } @Test - void findByCustomerIdReturnsOnlyThatCustomerOrders() { + void _02_ShouldReturnOnlyThatCustomerOrders_WhenFindingByCustomerId() { Order order = new Order(); order.setCustomer(customer); order.setProduct(product); @@ -100,7 +100,7 @@ void findByCustomerIdReturnsOnlyThatCustomerOrders() { } @Test - void findByProductIdReturnsOnlyThatProductOrders() { + void _03_ShouldReturnOnlyThatProductOrders_WhenFindingByProductId() { Order order = new Order(); order.setCustomer(customer); order.setProduct(product); @@ -114,7 +114,7 @@ void findByProductIdReturnsOnlyThatProductOrders() { } @Test - void findAllReturnsSavedOrders() { + void _04_ShouldReturnSavedOrders_WhenFindingAll() { Order order = new Order(); order.setCustomer(customer); order.setProduct(product); @@ -128,7 +128,7 @@ void findAllReturnsSavedOrders() { } @Test - void countReflectsSavedOrders() { + void _05_ShouldReflectSavedOrders_WhenCounting() { long before = orderDao.count(); Order order = new Order(); order.setCustomer(customer); @@ -143,7 +143,7 @@ void countReflectsSavedOrders() { } @Test - void deleteByIdRemovesTheOrder() { + void _06_ShouldRemoveOrder_WhenOrderIsDeletedById() { Order order = new Order(); order.setCustomer(customer); order.setProduct(product); From 7031a647da75f76edebdc0855a133d095e7d1a82 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:29:39 +0400 Subject: [PATCH 05/17] test(dao): rename ProductDaoTest methods to the _NN_Should_When convention Renames every test method of ProductDaoTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../edgareldy/springtutorial/dao/ProductDaoTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/ProductDaoTest.java b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/ProductDaoTest.java index dbda62b..ef42d6d 100644 --- a/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/ProductDaoTest.java +++ b/spring-tutorial-dao/src/test/java/edgareldy/springtutorial/dao/ProductDaoTest.java @@ -48,7 +48,7 @@ void createCategory() { } @Test - void saveAndFindByIdResolveTheCategoryRelation() { + void _01_ShouldResolveCategoryRelation_WhenProductIsSavedThenFoundById() { Product product = new Product(); product.setCategory(category); product.setProductName("Keyboard"); @@ -61,7 +61,7 @@ void saveAndFindByIdResolveTheCategoryRelation() { } @Test - void findByCategoryIdReturnsOnlyMatchingProducts() { + void _02_ShouldReturnOnlyMatchingProducts_WhenFindingByCategoryId() { Product product = new Product(); product.setCategory(category); product.setProductName("Mouse"); @@ -74,7 +74,7 @@ void findByCategoryIdReturnsOnlyMatchingProducts() { } @Test - void countByCategoryIdCountsOnlyThatCategoryProducts() { + void _03_ShouldCountOnlyThatCategoryProducts_WhenCountingByCategoryId() { Product product = new Product(); product.setCategory(category); product.setProductName("Monitor"); @@ -87,7 +87,7 @@ void countByCategoryIdCountsOnlyThatCategoryProducts() { } @Test - void findAllReturnsSavedProducts() { + void _04_ShouldReturnSavedProducts_WhenFindingAll() { Product product = new Product(); product.setCategory(category); product.setProductName("Webcam"); @@ -100,7 +100,7 @@ void findAllReturnsSavedProducts() { } @Test - void countReflectsSavedProducts() { + void _05_ShouldReflectSavedProducts_WhenCounting() { long before = productDao.count(); Product product = new Product(); product.setCategory(category); @@ -114,7 +114,7 @@ void countReflectsSavedProducts() { } @Test - void deleteByIdRemovesTheProduct() { + void _06_ShouldRemoveProduct_WhenProductIsDeletedById() { Product product = new Product(); product.setCategory(category); product.setProductName("Temporary"); From 86b96bfc7be3e5346337fa8a155bbb0654dd0a07 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:31:53 +0400 Subject: [PATCH 06/17] test(service): rename ServiceIntegrationTest methods to the _NN_Should_When convention Renames every test method of ServiceIntegrationTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../service/ServiceIntegrationTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/ServiceIntegrationTest.java b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/ServiceIntegrationTest.java index 58cacf3..baf44bd 100644 --- a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/ServiceIntegrationTest.java +++ b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/ServiceIntegrationTest.java @@ -70,14 +70,14 @@ class ServiceIntegrationTest { private ObjectProvider orderReferenceGeneratorProvider; @Test - void paginationDefaultsResolvesTheSpelComputedDefault() { + void _01_ShouldResolveSpelComputedDefault_WhenPaginationDefaultsAreInjected() { // #{10 * 2} evaluated by the real container, not overridden by // ReflectionTestUtils.setField as the unit tests do. assertEquals(20, paginationDefaults.pageSize(null)); } @Test - void orderReferenceGeneratorIsPrototypeScoped() { + void _02_ShouldReturnDistinctInstances_WhenOrderReferenceGeneratorIsPrototypeScoped() { OrderReferenceGenerator first = orderReferenceGeneratorProvider.getObject(); OrderReferenceGenerator second = orderReferenceGeneratorProvider.getObject(); @@ -85,7 +85,7 @@ void orderReferenceGeneratorIsPrototypeScoped() { } @Test - void creatingAnInvalidCategoryThrowsAConstraintViolation() { + void _03_ShouldThrowConstraintViolation_WhenCategoryIsInvalid() { Category category = new Category(); category.setCategoryName(" "); @@ -93,7 +93,7 @@ void creatingAnInvalidCategoryThrowsAConstraintViolation() { } @Test - void categoryWithProductsCannotBeDeleted() { + void _04_ShouldRejectDeletion_WhenCategoryStillHasProducts() { Category category = new Category(); category.setCategoryName("Electronics"); category = categoryService.create(category); @@ -108,7 +108,7 @@ void categoryWithProductsCannotBeDeleted() { } @Test - void creatingAnOrderResolvesCustomerAndProductAndComputesTotal() { + void _05_ShouldResolveCustomerAndProductAndComputeTotal_WhenOrderIsCreated() { Category category = new Category(); category.setCategoryName("Furniture"); category = categoryService.create(category); @@ -135,7 +135,7 @@ void creatingAnOrderResolvesCustomerAndProductAndComputesTotal() { } @Test - void updatingAnOrderRecomputesTheTotal() { + void _06_ShouldRecomputeTotal_WhenOrderIsUpdated() { Category category = new Category(); category.setCategoryName("Furniture"); category = categoryService.create(category); From e3746518ac9c12841ab39871eabf0da15f137131 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:31:53 +0400 Subject: [PATCH 07/17] test(service): rename CategoryServiceImplTest methods to the _NN_Should_When convention Renames every test method of CategoryServiceImplTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../service/impl/CategoryServiceImplTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CategoryServiceImplTest.java b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CategoryServiceImplTest.java index eda02ac..5e28ca7 100644 --- a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CategoryServiceImplTest.java +++ b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CategoryServiceImplTest.java @@ -48,7 +48,7 @@ void setUp() { } @Test - void findByIdReturnsTheCategoryWhenItExists() { + void _01_ShouldReturnCategory_WhenCategoryExists() { Category category = new Category(); category.setId(1L); category.setCategoryName("Electronics"); @@ -60,14 +60,14 @@ void findByIdReturnsTheCategoryWhenItExists() { } @Test - void findByIdThrowsWhenTheCategoryDoesNotExist() { + void _02_ShouldThrowNotFound_WhenCategoryDoesNotExist() { when(categoryDao.findById(1L)).thenReturn(Optional.empty()); assertThrows(ResourceNotFoundException.class, () -> categoryService.findById(1L)); } @Test - void deleteRemovesTheCategoryWhenItHasNoProducts() { + void _03_ShouldRemoveCategory_WhenCategoryHasNoProducts() { Category category = new Category(); category.setId(1L); when(categoryDao.findById(1L)).thenReturn(Optional.of(category)); @@ -79,7 +79,7 @@ void deleteRemovesTheCategoryWhenItHasNoProducts() { } @Test - void deleteThrowsWhenTheCategoryStillHasProducts() { + void _04_ShouldThrowException_WhenCategoryStillHasProducts() { Category category = new Category(); category.setId(1L); when(categoryDao.findById(1L)).thenReturn(Optional.of(category)); @@ -91,21 +91,21 @@ void deleteThrowsWhenTheCategoryStillHasProducts() { } @Test - void findAllUsesTheDefaultPageSizeWhenNoneIsRequested() { + void _05_ShouldUseDefaultPageSize_WhenNoneIsRequested() { categoryService.findAll(0, null); verify(categoryDao).findAll(0, 20); } @Test - void countDelegatesToTheDao() { + void _06_ShouldDelegateToDao_WhenCounting() { when(categoryDao.count()).thenReturn(5L); assertEquals(5L, categoryService.count()); } @Test - void updateChangesTheCategoryNameOfTheExistingEntity() { + void _07_ShouldChangeCategoryName_WhenExistingCategoryIsUpdated() { Category existing = new Category(); existing.setId(1L); existing.setCategoryName("Old name"); @@ -120,7 +120,7 @@ void updateChangesTheCategoryNameOfTheExistingEntity() { } @Test - void updateThrowsWhenTheCategoryDoesNotExist() { + void _08_ShouldThrowNotFound_WhenUpdatedCategoryDoesNotExist() { when(categoryDao.findById(1L)).thenReturn(Optional.empty()); Category payload = new Category(); payload.setCategoryName("New name"); From 59e153eed3e1b569fd88c913b9ffacea3fbaf8cb Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:31:53 +0400 Subject: [PATCH 08/17] test(service): rename CustomerServiceImplTest methods to the _NN_Should_When convention Renames every test method of CustomerServiceImplTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../service/impl/CustomerServiceImplTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CustomerServiceImplTest.java b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CustomerServiceImplTest.java index 4b41c0a..78dfe1e 100644 --- a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CustomerServiceImplTest.java +++ b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/CustomerServiceImplTest.java @@ -55,7 +55,7 @@ private Customer validCustomer() { } @Test - void createSavesAValidCustomer() { + void _01_ShouldSaveCustomer_WhenCustomerIsValid() { Customer customer = validCustomer(); when(customerDao.save(customer)).thenReturn(customer); @@ -65,7 +65,7 @@ void createSavesAValidCustomer() { } @Test - void createRejectsACustomerWhoseLastNameMatchesTheFirstName() { + void _02_ShouldRejectCustomer_WhenLastNameMatchesFirstName() { Customer customer = validCustomer(); customer.setLastName("Grace"); @@ -75,21 +75,21 @@ void createRejectsACustomerWhoseLastNameMatchesTheFirstName() { } @Test - void deleteThrowsWhenTheCustomerDoesNotExist() { + void _03_ShouldThrowNotFound_WhenDeletedCustomerDoesNotExist() { when(customerDao.findById(1L)).thenReturn(Optional.empty()); assertThrows(ResourceNotFoundException.class, () -> customerService.delete(1L)); } @Test - void countDelegatesToTheDao() { + void _04_ShouldDelegateToDao_WhenCounting() { when(customerDao.count()).thenReturn(4L); assertEquals(4L, customerService.count()); } @Test - void updateAppliesTheNewValuesToTheExistingCustomer() { + void _05_ShouldApplyNewValues_WhenExistingCustomerIsUpdated() { Customer existing = new Customer(); existing.setId(1L); existing.setFirstName("Old"); @@ -109,7 +109,7 @@ void updateAppliesTheNewValuesToTheExistingCustomer() { } @Test - void updateRejectsACustomerWhoseLastNameMatchesTheFirstName() { + void _06_ShouldRejectUpdate_WhenLastNameMatchesFirstName() { Customer existing = new Customer(); existing.setId(1L); when(customerDao.findById(1L)).thenReturn(Optional.of(existing)); From f32e751950b24b4db7d11ef8b29f4e4c4bf3b5aa Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:31:53 +0400 Subject: [PATCH 09/17] test(service): rename OrderReferenceGeneratorTest methods to the _NN_Should_When convention Renames every test method of OrderReferenceGeneratorTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../service/impl/OrderReferenceGeneratorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderReferenceGeneratorTest.java b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderReferenceGeneratorTest.java index 31086cf..2f6489c 100644 --- a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderReferenceGeneratorTest.java +++ b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderReferenceGeneratorTest.java @@ -21,7 +21,7 @@ class OrderReferenceGeneratorTest { @Test - void generateUsesTheInjectedClockRatherThanTheSystemClock() { + void _01_ShouldUseInjectedClock_WhenReferenceIsGenerated() { Clock fixedClock = Clock.fixed(Instant.parse("2026-01-01T00:00:00Z"), ZoneOffset.UTC); OrderReferenceGenerator generator = new OrderReferenceGenerator(fixedClock); From 724dcf7d121f5679f34981d6fba77c7e5e82d4f4 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:31:53 +0400 Subject: [PATCH 10/17] test(service): rename OrderServiceImplTest methods to the _NN_Should_When convention Renames every test method of OrderServiceImplTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../springtutorial/service/impl/OrderServiceImplTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderServiceImplTest.java b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderServiceImplTest.java index 1adb8fb..af6db3d 100644 --- a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderServiceImplTest.java +++ b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/OrderServiceImplTest.java @@ -64,7 +64,7 @@ void setUp() { } @Test - void createComputesTotalAndPublishesAnEvent() { + void _01_ShouldComputeTotalAndPublishEvent_WhenOrderIsCreated() { Customer customer = new Customer(); customer.setId(1L); Product product = new Product(); @@ -91,7 +91,7 @@ void createComputesTotalAndPublishesAnEvent() { } @Test - void createAsksTheProviderForAFreshOrderReferenceGeneratorEachTime() { + void _02_ShouldAskProviderForFreshGenerator_WhenOrderIsCreated() { Customer customer = new Customer(); customer.setId(1L); Product product = new Product(); @@ -112,14 +112,14 @@ void createAsksTheProviderForAFreshOrderReferenceGeneratorEachTime() { } @Test - void countDelegatesToTheDao() { + void _03_ShouldDelegateToDao_WhenCounting() { when(orderDao.count()).thenReturn(11L); assertEquals(11L, orderService.count()); } @Test - void updateResolvesCustomerAndProductAgainAndRecomputesTheTotal() { + void _04_ShouldResolveAgainAndRecomputeTotal_WhenOrderIsUpdated() { Order existing = new Order(); existing.setId(9L); Customer customer = new Customer(); From 9d5dc083f6c31d91dcb0d8f99d56dd487e2f24e3 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:31:53 +0400 Subject: [PATCH 11/17] test(service): rename ProductServiceImplTest methods to the _NN_Should_When convention Renames every test method of ProductServiceImplTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../service/impl/ProductServiceImplTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/ProductServiceImplTest.java b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/ProductServiceImplTest.java index e2580bf..ad7e55c 100644 --- a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/ProductServiceImplTest.java +++ b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/impl/ProductServiceImplTest.java @@ -45,7 +45,7 @@ void setUp() { } @Test - void createResolvesTheCategoryBeforeSaving() { + void _01_ShouldResolveCategoryBeforeSaving_WhenProductIsCreated() { Category category = new Category(); category.setId(1L); when(categoryDao.findById(1L)).thenReturn(Optional.of(category)); @@ -60,7 +60,7 @@ void createResolvesTheCategoryBeforeSaving() { } @Test - void createThrowsWhenTheCategoryDoesNotExist() { + void _02_ShouldThrowNotFound_WhenProductCategoryDoesNotExist() { when(categoryDao.findById(1L)).thenReturn(Optional.empty()); Product product = new Product(); @@ -68,35 +68,35 @@ void createThrowsWhenTheCategoryDoesNotExist() { } @Test - void findAllWithoutCategoryIdDelegatesToFindAll() { + void _03_ShouldDelegateToFindAll_WhenNoCategoryIdIsGiven() { productService.findAll(null, 0, 5); verify(productDao).findAll(0, 5); } @Test - void findAllWithCategoryIdDelegatesToFindByCategoryId() { + void _04_ShouldDelegateToFindByCategoryId_WhenCategoryIdIsGiven() { productService.findAll(1L, 0, 5); verify(productDao).findByCategoryId(1L, 0, 5); } @Test - void countWithoutCategoryIdDelegatesToCount() { + void _05_ShouldDelegateToCount_WhenNoCategoryIdIsGiven() { when(productDao.count()).thenReturn(7L); assertEquals(7L, productService.count(null)); } @Test - void countWithCategoryIdDelegatesToCountByCategoryId() { + void _06_ShouldDelegateToCountByCategoryId_WhenCategoryIdIsGiven() { when(productDao.countByCategoryId(1L)).thenReturn(3L); assertEquals(3L, productService.count(1L)); } @Test - void updateResolvesTheCategoryAgainAndAppliesTheNewValues() { + void _07_ShouldResolveCategoryAgainAndApplyNewValues_WhenProductIsUpdated() { Product existing = new Product(); existing.setId(1L); Category newCategory = new Category(); From 9808058ab4755a818b4b262ac26e64ee8521c93b Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:31:53 +0400 Subject: [PATCH 12/17] test(service): rename CustomerValidatorTest methods to the _NN_Should_When convention Renames every test method of CustomerValidatorTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../service/validation/CustomerValidatorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/validation/CustomerValidatorTest.java b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/validation/CustomerValidatorTest.java index a6ffbf2..7a257b9 100644 --- a/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/validation/CustomerValidatorTest.java +++ b/spring-tutorial-service/src/test/java/edgareldy/springtutorial/service/validation/CustomerValidatorTest.java @@ -21,7 +21,7 @@ class CustomerValidatorTest { private final CustomerValidator validator = new CustomerValidator(); @Test - void rejectsWhenLastNameMatchesFirstNameIgnoringCase() { + void _01_ShouldReject_WhenLastNameMatchesFirstNameIgnoringCase() { Customer customer = new Customer(); customer.setFirstName("Grace"); customer.setLastName("GRACE"); @@ -33,7 +33,7 @@ void rejectsWhenLastNameMatchesFirstNameIgnoringCase() { } @Test - void acceptsDistinctFirstAndLastNames() { + void _02_ShouldAccept_WhenFirstAndLastNamesAreDistinct() { Customer customer = new Customer(); customer.setFirstName("Grace"); customer.setLastName("Hopper"); From c7888516a355914be97550097e91aa7d6839499b Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:32:51 +0400 Subject: [PATCH 13/17] test(web): rename WebIntegrationTest methods to the _NN_Should_When convention Renames every test method of WebIntegrationTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../java/edgareldy/springtutorial/web/WebIntegrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/WebIntegrationTest.java b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/WebIntegrationTest.java index faf3a48..a3b46ef 100644 --- a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/WebIntegrationTest.java +++ b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/WebIntegrationTest.java @@ -90,7 +90,7 @@ private Long createCustomer() throws Exception { } @Test - void listingAndReadingOrdersResolvesTheNestedCategoryWithoutError() throws Exception { + void _01_ShouldResolveNestedCategoryWithoutError_WhenOrdersAreListedAndRead() throws Exception { Long categoryId = createCategory("Books"); Long productId = createProduct("Clean Code", 42.0, categoryId); Long customerId = createCustomer(); @@ -123,7 +123,7 @@ void listingAndReadingOrdersResolvesTheNestedCategoryWithoutError() throws Excep } @Test - void validationMessageIsResolvedThroughTheRealMessageSourcePerLocale() throws Exception { + void _02_ShouldResolveValidationMessagePerLocale_WhenRealMessageSourceIsUsed() throws Exception { String invalidBody = objectMapper.writeValueAsString(new CategoryPayload("")); String englishMessage = mockMvc.perform(post("/api/v1/categories") From 62c8ffb76e39c3c2cdd852d24b5463eeea1643d5 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:32:51 +0400 Subject: [PATCH 14/17] test(web): rename CategoryControllerTest methods to the _NN_Should_When convention Renames every test method of CategoryControllerTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../web/controller/CategoryControllerTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CategoryControllerTest.java b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CategoryControllerTest.java index 34af0fb..16aa77c 100644 --- a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CategoryControllerTest.java +++ b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CategoryControllerTest.java @@ -71,7 +71,7 @@ private JsonNode bodyOf(MvcResult result) throws Exception { } @Test - void findAllReturnsAPagedApiResponse() throws Exception { + void _01_ShouldReturnPagedApiResponse_WhenCategoriesAreListed() throws Exception { Category category = new Category(); category.setId(1L); category.setCategoryName("Books"); @@ -88,7 +88,7 @@ void findAllReturnsAPagedApiResponse() throws Exception { } @Test - void findByIdReturnsNotFoundWhenMissing() throws Exception { + void _02_ShouldReturnNotFound_WhenCategoryIsMissing() throws Exception { when(categoryService.findById(99L)).thenThrow(new ResourceNotFoundException("Category 99 not found")); MvcResult result = mockMvc.perform(get("/api/v1/categories/{id}", 99L)) @@ -99,7 +99,7 @@ void findByIdReturnsNotFoundWhenMissing() throws Exception { } @Test - void createReturnsCreatedWithTheMappedResponse() throws Exception { + void _03_ShouldReturnCreatedWithMappedResponse_WhenCategoryIsCreated() throws Exception { Category created = new Category(); created.setId(1L); created.setCategoryName("Books"); @@ -115,7 +115,7 @@ void createReturnsCreatedWithTheMappedResponse() throws Exception { } @Test - void createRejectsABlankCategoryName() throws Exception { + void _04_ShouldRejectCreation_WhenCategoryNameIsBlank() throws Exception { mockMvc.perform(post("/api/v1/categories") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(new CategoryPayload("")))) @@ -123,7 +123,7 @@ void createRejectsABlankCategoryName() throws Exception { } @Test - void updateDelegatesToTheService() throws Exception { + void _05_ShouldDelegateToService_WhenCategoryIsUpdated() throws Exception { Category updated = new Category(); updated.setId(1L); updated.setCategoryName("Renamed"); @@ -139,7 +139,7 @@ void updateDelegatesToTheService() throws Exception { } @Test - void deleteDelegatesToTheService() throws Exception { + void _06_ShouldDelegateToService_WhenCategoryIsDeleted() throws Exception { mockMvc.perform(delete("/api/v1/categories/{id}", 1L)).andExpect(status().isOk()); verify(categoryService).delete(1L); From 1d81e38ed684d4420f61db1b2c33e58f51342b82 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:32:51 +0400 Subject: [PATCH 15/17] test(web): rename CustomerControllerTest methods to the _NN_Should_When convention Renames every test method of CustomerControllerTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../web/controller/CustomerControllerTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CustomerControllerTest.java b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CustomerControllerTest.java index f4d3800..ca8c97e 100644 --- a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CustomerControllerTest.java +++ b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/CustomerControllerTest.java @@ -77,7 +77,7 @@ private JsonNode bodyOf(MvcResult result) throws Exception { } @Test - void findAllReturnsAPagedApiResponse() throws Exception { + void _01_ShouldReturnPagedApiResponse_WhenCustomersAreListed() throws Exception { when(customerService.findAll(anyInt(), any())).thenReturn(List.of(customerFixture())); when(customerService.count()).thenReturn(1L); @@ -88,14 +88,14 @@ void findAllReturnsAPagedApiResponse() throws Exception { } @Test - void findByIdReturnsNotFoundWhenMissing() throws Exception { + void _02_ShouldReturnNotFound_WhenCustomerIsMissing() throws Exception { when(customerService.findById(99L)).thenThrow(new ResourceNotFoundException("Customer 99 not found")); mockMvc.perform(get("/api/v1/customers/{id}", 99L)).andExpect(status().isNotFound()); } @Test - void createDelegatesToTheService() throws Exception { + void _03_ShouldDelegateToService_WhenCustomerIsCreated() throws Exception { when(customerService.create(any())).thenReturn(customerFixture()); MvcResult result = mockMvc.perform(post("/api/v1/customers") @@ -109,7 +109,7 @@ void createDelegatesToTheService() throws Exception { } @Test - void createRejectsAnInvalidEmail() throws Exception { + void _04_ShouldRejectCreation_WhenEmailIsInvalid() throws Exception { mockMvc.perform(post("/api/v1/customers") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString( @@ -118,7 +118,7 @@ void createRejectsAnInvalidEmail() throws Exception { } @Test - void updateDelegatesToTheService() throws Exception { + void _05_ShouldDelegateToService_WhenCustomerIsUpdated() throws Exception { when(customerService.update(eq(1L), any())).thenReturn(customerFixture()); mockMvc.perform(put("/api/v1/customers/{id}", 1L) @@ -129,7 +129,7 @@ void updateDelegatesToTheService() throws Exception { } @Test - void deleteDelegatesToTheService() throws Exception { + void _06_ShouldDelegateToService_WhenCustomerIsDeleted() throws Exception { mockMvc.perform(delete("/api/v1/customers/{id}", 1L)).andExpect(status().isOk()); verify(customerService).delete(1L); From 4f7a7685b43381cee64d12df0969351e61de3d5b Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:32:51 +0400 Subject: [PATCH 16/17] test(web): rename OrderControllerTest methods to the _NN_Should_When convention Renames every test method of OrderControllerTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../web/controller/OrderControllerTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/OrderControllerTest.java b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/OrderControllerTest.java index 474db72..f56a439 100644 --- a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/OrderControllerTest.java +++ b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/OrderControllerTest.java @@ -98,7 +98,7 @@ private JsonNode bodyOf(MvcResult result) throws Exception { } @Test - void findAllReturnsAPagedApiResponse() throws Exception { + void _01_ShouldReturnPagedApiResponse_WhenOrdersAreListed() throws Exception { when(orderService.findAll(isNull(), isNull(), anyInt(), any())).thenReturn(List.of(orderFixture())); when(orderService.count()).thenReturn(1L); @@ -109,14 +109,14 @@ void findAllReturnsAPagedApiResponse() throws Exception { } @Test - void findByIdReturnsNotFoundWhenMissing() throws Exception { + void _02_ShouldReturnNotFound_WhenOrderIsMissing() throws Exception { when(orderService.findById(99L)).thenThrow(new ResourceNotFoundException("Order 99 not found")); mockMvc.perform(get("/api/v1/orders/{id}", 99L)).andExpect(status().isNotFound()); } @Test - void createDelegatesToTheServiceWithTheResolvedCustomerAndProductIds() throws Exception { + void _03_ShouldDelegateWithResolvedCustomerAndProductIds_WhenOrderIsCreated() throws Exception { when(orderService.create(any(), eq(1L), eq(1L))).thenReturn(orderFixture()); MvcResult result = mockMvc.perform(post("/api/v1/orders") @@ -129,7 +129,7 @@ void createDelegatesToTheServiceWithTheResolvedCustomerAndProductIds() throws Ex } @Test - void createRejectsANonPositiveQuantity() throws Exception { + void _04_ShouldRejectCreation_WhenQuantityIsNotPositive() throws Exception { mockMvc.perform(post("/api/v1/orders") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(new OrderPayload(0, 1L, 1L)))) @@ -137,7 +137,7 @@ void createRejectsANonPositiveQuantity() throws Exception { } @Test - void updateDelegatesToTheService() throws Exception { + void _05_ShouldDelegateToService_WhenOrderIsUpdated() throws Exception { when(orderService.update(eq(1L), any(), eq(1L), eq(1L))).thenReturn(orderFixture()); mockMvc.perform(put("/api/v1/orders/{id}", 1L) @@ -147,7 +147,7 @@ void updateDelegatesToTheService() throws Exception { } @Test - void deleteDelegatesToTheService() throws Exception { + void _06_ShouldDelegateToService_WhenOrderIsDeleted() throws Exception { mockMvc.perform(delete("/api/v1/orders/{id}", 1L)).andExpect(status().isOk()); verify(orderService).delete(1L); From f899af5565d094bfe85abfb51036a085bd3d7158 Mon Sep 17 00:00:00 2001 From: EdgarEldy Date: Mon, 21 Sep 2026 19:32:51 +0400 Subject: [PATCH 17/17] test(web): rename ProductControllerTest methods to the _NN_Should_When convention Renames every test method of ProductControllerTest to _NN_Should_When, with the sequence number following the order of the methods in the class; only the method names change, not the tests' logic or behavior. --- .../web/controller/ProductControllerTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/ProductControllerTest.java b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/ProductControllerTest.java index 710da05..c2c1c4d 100644 --- a/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/ProductControllerTest.java +++ b/spring-tutorial-web/src/test/java/edgareldy/springtutorial/web/controller/ProductControllerTest.java @@ -81,7 +81,7 @@ private JsonNode bodyOf(MvcResult result) throws Exception { } @Test - void findAllReturnsAPagedApiResponse() throws Exception { + void _01_ShouldReturnPagedApiResponse_WhenProductsAreListed() throws Exception { when(productService.findAll(isNull(), anyInt(), any())).thenReturn(List.of(productFixture())); when(productService.count(isNull())).thenReturn(1L); @@ -94,14 +94,14 @@ void findAllReturnsAPagedApiResponse() throws Exception { } @Test - void findByIdReturnsNotFoundWhenMissing() throws Exception { + void _02_ShouldReturnNotFound_WhenProductIsMissing() throws Exception { when(productService.findById(99L)).thenThrow(new ResourceNotFoundException("Product 99 not found")); mockMvc.perform(get("/api/v1/products/{id}", 99L)).andExpect(status().isNotFound()); } @Test - void createDelegatesToTheServiceWithTheResolvedCategoryId() throws Exception { + void _03_ShouldDelegateWithResolvedCategoryId_WhenProductIsCreated() throws Exception { when(productService.create(any(), eq(1L))).thenReturn(productFixture()); MvcResult result = mockMvc.perform(post("/api/v1/products") @@ -114,7 +114,7 @@ void createDelegatesToTheServiceWithTheResolvedCategoryId() throws Exception { } @Test - void createRejectsANonPositivePrice() throws Exception { + void _04_ShouldRejectCreation_WhenPriceIsNotPositive() throws Exception { mockMvc.perform(post("/api/v1/products") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(new ProductPayload("Clean Code", -1.0, 1L)))) @@ -122,7 +122,7 @@ void createRejectsANonPositivePrice() throws Exception { } @Test - void updateDelegatesToTheService() throws Exception { + void _05_ShouldDelegateToService_WhenProductIsUpdated() throws Exception { when(productService.update(eq(1L), any(), eq(1L))).thenReturn(productFixture()); mockMvc.perform(put("/api/v1/products/{id}", 1L) @@ -132,7 +132,7 @@ void updateDelegatesToTheService() throws Exception { } @Test - void deleteDelegatesToTheService() throws Exception { + void _06_ShouldDelegateToService_WhenProductIsDeleted() throws Exception { mockMvc.perform(delete("/api/v1/products/{id}", 1L)).andExpect(status().isOk()); verify(productService).delete(1L);