Skip to content

Commit bc33abb

Browse files
committed
test: adopt BDD Given-When-Then pattern across all tests
- Rename all test methods to givenX_whenY_thenZ() format - Replace AssertJ assertThat() with BDDAssertions then() - Update code comments from Arrange/Act/Assert to Given/When/Then - Apply semantic variable naming (expected/actual for assertions, dto/entity for setup) - Eliminate magic strings/numbers from assertions - Update all JavaDocs to abstract business descriptions - Ensure comprehensive object comparisons using usingRecursiveComparison() - Reorder test methods to match controller/service method order - Refactor searchBySquadNumber to retrieveBySquadNumber (RESTful GET pattern)
1 parent 1bc2d5a commit bc33abb

6 files changed

Lines changed: 577 additions & 559 deletions

File tree

src/main/java/ar/com/nanotaboada/java/samples/spring/boot/controllers/PlayersController.java

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
* <li><b>GET</b> {@code /players} - Retrieve all players</li>
4040
* <li><b>GET</b> {@code /players/{id}} - Retrieve player by ID</li>
4141
* <li><b>GET</b> {@code /players/search/league/{league}} - Search players by league name</li>
42-
* <li><b>GET</b> {@code /players/search/squadnumber/{number}} - Search player by squad number</li>
42+
* <li><b>GET</b> {@code /players/squadnumber/{number}} - Retrieve player by squad number</li>
4343
* <li><b>POST</b> {@code /players} - Create a new player</li>
4444
* <li><b>PUT</b> {@code /players/{id}} - Update an existing player</li>
4545
* <li><b>DELETE</b> {@code /players/{id}} - Delete a player by ID</li>
@@ -113,6 +113,24 @@ public ResponseEntity<Void> post(@RequestBody @Valid PlayerDTO playerDTO) {
113113
* -----------------------------------------------------------------------------------------------------------------------
114114
*/
115115

116+
/**
117+
* Retrieves all players in the squad.
118+
* <p>
119+
* Returns the complete Argentina 2022 FIFA World Cup squad (26 players).
120+
* </p>
121+
*
122+
* @return 200 OK with array of all players (empty array if none found)
123+
*/
124+
@GetMapping("/players")
125+
@Operation(summary = "Retrieves all players")
126+
@ApiResponses(value = {
127+
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", schema = @Schema(implementation = PlayerDTO[].class)))
128+
})
129+
public ResponseEntity<List<PlayerDTO>> getAll() {
130+
List<PlayerDTO> players = playersService.retrieveAll();
131+
return ResponseEntity.status(HttpStatus.OK).body(players);
132+
}
133+
116134
/**
117135
* Retrieves a single player by their unique identifier.
118136
*
@@ -133,21 +151,26 @@ public ResponseEntity<PlayerDTO> getById(@PathVariable Long id) {
133151
}
134152

135153
/**
136-
* Retrieves all players in the squad.
154+
* Retrieves a player by their squad number (unique identifier).
137155
* <p>
138-
* Returns the complete Argentina 2022 FIFA World Cup squad (26 players).
156+
* Squad numbers are unique jersey numbers (e.g., Messi is #10). This is a direct lookup similar to getById().
157+
* Example: {@code /players/squadnumber/10} returns Lionel Messi
139158
* </p>
140159
*
141-
* @return 200 OK with array of all players (empty array if none found)
160+
* @param squadNumber the squad number to retrieve (jersey number, typically 1-99)
161+
* @return 200 OK with player data, or 404 Not Found if no player has that number
142162
*/
143-
@GetMapping("/players")
144-
@Operation(summary = "Retrieves all players")
163+
@GetMapping("/players/squadnumber/{squadNumber}")
164+
@Operation(summary = "Retrieves a player by squad number")
145165
@ApiResponses(value = {
146-
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", schema = @Schema(implementation = PlayerDTO[].class)))
166+
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", schema = @Schema(implementation = PlayerDTO.class))),
167+
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
147168
})
148-
public ResponseEntity<List<PlayerDTO>> getAll() {
149-
List<PlayerDTO> players = playersService.retrieveAll();
150-
return ResponseEntity.status(HttpStatus.OK).body(players);
169+
public ResponseEntity<PlayerDTO> getBySquadNumber(@PathVariable Integer squadNumber) {
170+
PlayerDTO player = playersService.retrieveBySquadNumber(squadNumber);
171+
return (player != null)
172+
? ResponseEntity.status(HttpStatus.OK).body(player)
173+
: ResponseEntity.status(HttpStatus.NOT_FOUND).build();
151174
}
152175

153176
/**
@@ -169,30 +192,6 @@ public ResponseEntity<List<PlayerDTO>> searchByLeague(@PathVariable String leagu
169192
return ResponseEntity.status(HttpStatus.OK).body(players);
170193
}
171194

172-
/**
173-
* Searches for a player by their squad number.
174-
* <p>
175-
* Squad numbers are jersey numbers that users recognize (e.g., Messi is #10).
176-
* Example: {@code /players/search/squadnumber/10} returns Lionel Messi
177-
* </p>
178-
*
179-
* @param squadNumber the squad number to search for (jersey number, typically 1-99)
180-
* @return 200 OK with player data, or 404 Not Found if no player has that
181-
* number
182-
*/
183-
@GetMapping("/players/search/squadnumber/{squadNumber}")
184-
@Operation(summary = "Searches for a player by squad number")
185-
@ApiResponses(value = {
186-
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", schema = @Schema(implementation = PlayerDTO.class))),
187-
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
188-
})
189-
public ResponseEntity<PlayerDTO> searchBySquadNumber(@PathVariable Integer squadNumber) {
190-
PlayerDTO player = playersService.searchBySquadNumber(squadNumber);
191-
return (player != null)
192-
? ResponseEntity.status(HttpStatus.OK).body(player)
193-
: ResponseEntity.status(HttpStatus.NOT_FOUND).build();
194-
}
195-
196195
/*
197196
* -----------------------------------------------------------------------------------------------------------------------
198197
* HTTP PUT

src/main/java/ar/com/nanotaboada/java/samples/spring/boot/repositories/PlayersRepository.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,6 @@
4040
@Repository
4141
public interface PlayersRepository extends JpaRepository<Player, Long> {
4242

43-
/**
44-
* Finds players by league name using case-insensitive wildcard matching.
45-
* <p>
46-
* This method uses a custom JPQL query with LIKE operator for partial matches.
47-
* For example, searching for "Premier" will match "Premier League".
48-
* </p>
49-
*
50-
* @param league the league name to search for (partial matches allowed)
51-
* @return a list of players whose league name contains the search term (empty
52-
* list if none found)
53-
*/
54-
@Query("SELECT p FROM Player p WHERE LOWER(p.league) LIKE LOWER(CONCAT('%', :league, '%'))")
55-
List<Player> findByLeagueContainingIgnoreCase(@Param("league") String league);
56-
5743
/**
5844
* Finds a player by their squad number (exact match).
5945
* <p>
@@ -67,4 +53,18 @@ public interface PlayersRepository extends JpaRepository<Player, Long> {
6753
* @return an Optional containing the player if found, empty Optional otherwise
6854
*/
6955
Optional<Player> findBySquadNumber(Integer squadNumber);
56+
57+
/**
58+
* Finds players by league name using case-insensitive wildcard matching.
59+
* <p>
60+
* This method uses a custom JPQL query with LIKE operator for partial matches.
61+
* For example, searching for "Premier" will match "Premier League".
62+
* </p>
63+
*
64+
* @param league the league name to search for (partial matches allowed)
65+
* @return a list of players whose league name contains the search term (empty
66+
* list if none found)
67+
*/
68+
@Query("SELECT p FROM Player p WHERE LOWER(p.league) LIKE LOWER(CONCAT('%', :league, '%'))")
69+
List<Player> findByLeagueContainingIgnoreCase(@Param("league") String league);
7070
}

src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/PlayersService.java

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,24 @@ public PlayerDTO create(PlayerDTO playerDTO) {
110110
* -----------------------------------------------------------------------------------------------------------------------
111111
*/
112112

113+
/**
114+
* Retrieves all players from the database.
115+
* <p>
116+
* This method returns the complete Argentina 2022 FIFA World Cup squad (26 players).
117+
* Results are cached to improve performance on subsequent calls.
118+
* </p>
119+
*
120+
* @return a list of all players (empty list if none found)
121+
* @see org.springframework.cache.annotation.Cacheable
122+
*/
123+
@Cacheable(value = "players")
124+
public List<PlayerDTO> retrieveAll() {
125+
return playersRepository.findAll()
126+
.stream()
127+
.map(this::mapFrom)
128+
.toList();
129+
}
130+
113131
/**
114132
* Retrieves a player by their unique identifier.
115133
* <p>
@@ -130,21 +148,21 @@ public PlayerDTO retrieveById(Long id) {
130148
}
131149

132150
/**
133-
* Retrieves all players from the database.
151+
* Retrieves a player by their squad number (unique identifier).
134152
* <p>
135-
* This method returns the complete Argentina 2022 FIFA World Cup squad (26 players).
136-
* Results are cached to improve performance on subsequent calls.
153+
* Squad numbers are unique jersey numbers (e.g., Messi is #10). This is a direct lookup by unique identifier,
154+
* similar to retrieveById(). Results are cached to improve performance.
137155
* </p>
138156
*
139-
* @return a list of all players (empty list if none found)
157+
* @param squadNumber the squad number to retrieve (jersey number, typically 1-99)
158+
* @return the player DTO if found, null otherwise
140159
* @see org.springframework.cache.annotation.Cacheable
141160
*/
142-
@Cacheable(value = "players")
143-
public List<PlayerDTO> retrieveAll() {
144-
return playersRepository.findAll()
145-
.stream()
161+
@Cacheable(value = "players", key = "'squad-' + #squadNumber", unless = "#result == null")
162+
public PlayerDTO retrieveBySquadNumber(Integer squadNumber) {
163+
return playersRepository.findBySquadNumber(squadNumber)
146164
.map(this::mapFrom)
147-
.toList();
165+
.orElse(null);
148166
}
149167

150168
/*
@@ -170,22 +188,6 @@ public List<PlayerDTO> searchByLeague(String league) {
170188
.toList();
171189
}
172190

173-
/**
174-
* Searches for a player by their squad number.
175-
* <p>
176-
* This method performs an exact match on the squad number field. Squad numbers are jersey numbers that users recognize
177-
* (e.g., Messi is #10).
178-
* </p>
179-
*
180-
* @param squadNumber the squad number to search for (jersey number, typically 1-99)
181-
* @return the player DTO if found, null otherwise
182-
*/
183-
public PlayerDTO searchBySquadNumber(Integer squadNumber) {
184-
return playersRepository.findBySquadNumber(squadNumber)
185-
.map(this::mapFrom)
186-
.orElse(null);
187-
}
188-
189191
/*
190192
* -----------------------------------------------------------------------------------------------------------------------
191193
* Update

0 commit comments

Comments
 (0)