Skip to content

Commit 18a5c59

Browse files
nanotaboadaCopilot
andcommitted
refactor(tests): rename test methods to Microsoft naming standard (#396)
- Apply {HttpMethod}_{Resource}_{Condition}_Returns{Outcome} to PlayerControllerTests (PascalCase per .NET conventions) - Apply {MethodName}_{StateUnderTest}_{ExpectedBehavior} to PlayerServiceTests and PlayerValidatorTests - Fix broken 201 assertion in Post_Players_NonExisting_Returns201Created - Add UpdateAsync_PlayerNotFound and DeleteAsync_PlayerNotFound (service) - Add DateOfBirthToday and DateOfBirthOnJanuary1st1900 boundary tests Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent c727528 commit 18a5c59

5 files changed

Lines changed: 164 additions & 48 deletions

File tree

.github/copilot-instructions.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ test/Dotnet.Samples.AspNetCore.WebApi.Tests/
4646
- **Reads**: Use `AsNoTracking()` for all EF Core read queries
4747
- **Errors**: RFC 7807 Problem Details for all error responses
4848
- **Logging**: Structured logging via `ILogger<T>`; never `Console.Write`
49-
- **Tests**: xUnit + Moq + FluentAssertions; test naming mirrors method under test
49+
- **Tests**: xUnit + Moq + FluentAssertions; naming convention per layer:
50+
- Controller: `{HttpMethod}_{Resource}_{Condition}_Returns{Outcome}` (e.g. `Get_Players_Existing_ReturnsPlayers`)
51+
- Service / Validator: `{MethodName}_{StateUnderTest}_{ExpectedBehavior}` (e.g. `RetrieveAsync_CacheMiss_QueriesRepositoryAndCachesResult`)
5052
- **Avoid**: synchronous EF Core APIs, controller business logic, static service/repository classes
5153

5254
## Commands

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,18 @@ This project uses famous football stadiums (A-Z) that hosted FIFA World Cup matc
4646

4747
### Changed
4848

49+
- Rename test methods to follow Microsoft .NET naming standard (#396)
50+
4951
### Deprecated
5052

5153
### Removed
5254

5355
### Fixed
5456

57+
- Fix broken 201 assertion in controller test for `Post_Players_NonExisting` (#396)
58+
- Add missing edge case tests for `UpdateAsync`, `DeleteAsync`, and
59+
`DateOfBirth` boundary validation (#396)
60+
5561
### Security
5662

5763
---

test/Dotnet.Samples.AspNetCore.WebApi.Tests/Unit/PlayerControllerTests.cs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public PlayerControllerTests()
2424

2525
[Fact]
2626
[Trait("Category", "Unit")]
27-
public async Task GivenPostAsync_WhenValidatorReturnsErrors_ThenResponseStatusCodeShouldBe400BadRequest()
27+
public async Task Post_Players_ValidationError_Returns400BadRequest()
2828
{
2929
// Arrange
3030
var request = PlayerFakes.MakeRequestModelForCreate();
@@ -65,7 +65,7 @@ public async Task GivenPostAsync_WhenValidatorReturnsErrors_ThenResponseStatusCo
6565

6666
[Fact]
6767
[Trait("Category", "Unit")]
68-
public async Task GivenPostAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsPlayer_ThenResponseStatusCodeShouldBe409Conflict()
68+
public async Task Post_Players_Existing_Returns409Conflict()
6969
{
7070
// Arrange
7171
var request = PlayerFakes.MakeRequestModelForCreate();
@@ -108,7 +108,7 @@ public async Task GivenPostAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsPla
108108

109109
[Fact]
110110
[Trait("Category", "Unit")]
111-
public async Task GivenPostAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsNull_ThenResponseStatusCodeShouldBe201Created()
111+
public async Task Post_Players_NonExisting_Returns201Created()
112112
{
113113
// Arrange
114114
var request = PlayerFakes.MakeRequestModelForCreate();
@@ -146,13 +146,13 @@ public async Task GivenPostAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsNul
146146
),
147147
Times.Once
148148
);
149-
if (result is CreatedAtRoute<PlayerRequestModel> httpResult)
149+
if (result is CreatedAtRoute<PlayerResponseModel> httpResult)
150150
{
151-
httpResult.Should().NotBeNull().And.BeOfType<Created<PlayerResponseModel>>();
151+
httpResult.Should().NotBeNull().And.BeOfType<CreatedAtRoute<PlayerResponseModel>>();
152152
httpResult.StatusCode.Should().Be(StatusCodes.Status201Created);
153153
httpResult.Value.Should().BeEquivalentTo(response);
154-
httpResult.RouteName.Should().Be("GetById");
155-
httpResult.RouteValues.Should().NotBeNull().And.ContainKey("id");
154+
httpResult.RouteName.Should().Be("RetrieveBySquadNumber");
155+
httpResult.RouteValues.Should().NotBeNull().And.ContainKey("squadNumber");
156156
}
157157
}
158158

@@ -162,7 +162,7 @@ public async Task GivenPostAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsNul
162162

163163
[Fact]
164164
[Trait("Category", "Unit")]
165-
public async Task GivenGetAsync_WhenServiceRetrieveAsyncReturnsListOfPlayers_ThenResponseShouldBeEquivalentToListOfPlayers()
165+
public async Task Get_Players_Existing_ReturnsPlayers()
166166
{
167167
// Arrange
168168
var response = PlayerFakes.MakeResponseModelsForRetrieve();
@@ -187,7 +187,7 @@ public async Task GivenGetAsync_WhenServiceRetrieveAsyncReturnsListOfPlayers_The
187187

188188
[Fact]
189189
[Trait("Category", "Unit")]
190-
public async Task GivenGetAsync_WhenServiceRetrieveAsyncReturnsEmptyList_ThenResponseStatusCodeShouldBe404NotFound()
190+
public async Task Get_Players_NonExisting_Returns404NotFound()
191191
{
192192
// Arrange
193193
var (service, logger, validator) = PlayerMocks.InitControllerMocks();
@@ -209,7 +209,7 @@ public async Task GivenGetAsync_WhenServiceRetrieveAsyncReturnsEmptyList_ThenRes
209209

210210
[Fact]
211211
[Trait("Category", "Unit")]
212-
public async Task GivenGetByIdAsync_WhenServiceRetrieveByIdAsyncReturnsNull_ThenResponseStatusCodeShouldBe404NotFound()
212+
public async Task Get_PlayerById_NonExisting_Returns404NotFound()
213213
{
214214
// Arrange
215215
var id = Guid.NewGuid();
@@ -234,7 +234,7 @@ public async Task GivenGetByIdAsync_WhenServiceRetrieveByIdAsyncReturnsNull_Then
234234

235235
[Fact]
236236
[Trait("Category", "Unit")]
237-
public async Task GivenGetByIdAsync_WhenServiceRetrieveByIdAsyncReturnsPlayer_ThenResponseStatusCodeShouldBe200Ok()
237+
public async Task Get_PlayerById_Existing_Returns200OK()
238238
{
239239
// Arrange
240240
var id = Guid.NewGuid();
@@ -260,7 +260,7 @@ public async Task GivenGetByIdAsync_WhenServiceRetrieveByIdAsyncReturnsPlayer_Th
260260

261261
[Fact]
262262
[Trait("Category", "Unit")]
263-
public async Task GivenGetBySquadNumberAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsNull_ThenResponseStatusCodeShouldBe404NotFound()
263+
public async Task Get_PlayerBySquadNumber_NonExisting_Returns404NotFound()
264264
{
265265
// Arrange
266266
var squadNumber = 999;
@@ -285,7 +285,7 @@ public async Task GivenGetBySquadNumberAsync_WhenServiceRetrieveBySquadNumberAsy
285285

286286
[Fact]
287287
[Trait("Category", "Unit")]
288-
public async Task GivenGetBySquadNumberAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsPlayer_ThenResponseStatusCodeShouldBe200Ok()
288+
public async Task Get_PlayerBySquadNumber_Existing_Returns200OK()
289289
{
290290
// Arrange
291291
var squadNumber = 10;
@@ -317,7 +317,7 @@ public async Task GivenGetBySquadNumberAsync_WhenServiceRetrieveBySquadNumberAsy
317317

318318
[Fact]
319319
[Trait("Category", "Unit")]
320-
public async Task GivenPutAsync_WhenValidatorReturnsErrors_ThenResponseStatusCodeShouldBe400BadRequest()
320+
public async Task Put_PlayerBySquadNumber_ValidationError_Returns400BadRequest()
321321
{
322322
// Arrange
323323
var squadNumber = 20;
@@ -361,7 +361,7 @@ public async Task GivenPutAsync_WhenValidatorReturnsErrors_ThenResponseStatusCod
361361

362362
[Fact]
363363
[Trait("Category", "Unit")]
364-
public async Task GivenPutAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsNull_ThenResponseStatusCodeShouldBe404NotFound()
364+
public async Task Put_PlayerBySquadNumber_NonExisting_Returns404NotFound()
365365
{
366366
// Arrange
367367
var squadNumber = 999;
@@ -403,7 +403,7 @@ public async Task GivenPutAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsNull
403403

404404
[Fact]
405405
[Trait("Category", "Unit")]
406-
public async Task GivenPutAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsPlayer_ThenResponseStatusCodeShouldBe204NoContent()
406+
public async Task Put_PlayerBySquadNumber_Existing_Returns204NoContent()
407407
{
408408
// Arrange
409409
var squadNumber = 23;
@@ -446,7 +446,7 @@ public async Task GivenPutAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsPlay
446446

447447
[Fact]
448448
[Trait("Category", "Unit")]
449-
public async Task GivenDeleteAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsNull_ThenResponseStatusCodeShouldBe404NotFound()
449+
public async Task Delete_PlayerBySquadNumber_NonExisting_Returns404NotFound()
450450
{
451451
// Arrange
452452
var squadNumber = 999;
@@ -472,7 +472,7 @@ public async Task GivenDeleteAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsN
472472

473473
[Fact]
474474
[Trait("Category", "Unit")]
475-
public async Task GivenDeleteAsync_WhenServiceRetrieveBySquadNumberAsyncReturnsPlayer_ThenResponseStatusCodeShouldBe204NoContent()
475+
public async Task Delete_PlayerBySquadNumber_Existing_Returns204NoContent()
476476
{
477477
// Arrange
478478
var squadNumber = 26;

test/Dotnet.Samples.AspNetCore.WebApi.Tests/Unit/PlayerServiceTests.cs

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class PlayerServiceTests
1515

1616
[Fact]
1717
[Trait("Category", "Unit")]
18-
public async Task GivenCreateAsync_WhenRepositoryAddAsync_ThenAddsPlayerToRepositoryAndRemovesCache()
18+
public async Task CreateAsync_WhenCalled_AddsPlayerAndRemovesCache()
1919
{
2020
// Arrange
2121
var request = PlayerFakes.MakeRequestModelForCreate();
@@ -49,7 +49,7 @@ public async Task GivenCreateAsync_WhenRepositoryAddAsync_ThenAddsPlayerToReposi
4949

5050
[Fact]
5151
[Trait("Category", "Unit")]
52-
public async Task GivenRetrieveAsync_WhenRepositoryGetAllAsyncReturnsPlayers_ThenCacheCreateEntryAndResultShouldBeListOfPlayers()
52+
public async Task RetrieveAsync_CacheMiss_QueriesRepositoryAndCachesResult()
5353
{
5454
// Arrange
5555
var value = It.IsAny<object>();
@@ -83,7 +83,7 @@ public async Task GivenRetrieveAsync_WhenRepositoryGetAllAsyncReturnsPlayers_The
8383

8484
[Fact]
8585
[Trait("Category", "Unit")]
86-
public async Task GivenRetrieveAsync_WhenExecutedForTheSecondTime_ThenSecondExecutionTimeShouldBeLessThanFirst()
86+
public async Task RetrieveAsync_CacheHit_ReturnsBeforeQueryingRepository()
8787
{
8888
// Arrange
8989
var value = It.IsAny<object>();
@@ -121,7 +121,7 @@ public async Task GivenRetrieveAsync_WhenExecutedForTheSecondTime_ThenSecondExec
121121

122122
[Fact]
123123
[Trait("Category", "Unit")]
124-
public async Task GivenRetrieveByIdAsync_WhenRepositoryFindByIdAsyncReturnsNull_TheResultShouldBeNull()
124+
public async Task RetrieveByIdAsync_PlayerNotFound_ReturnsNull()
125125
{
126126
// Arrange
127127
var id = Guid.NewGuid();
@@ -147,7 +147,7 @@ public async Task GivenRetrieveByIdAsync_WhenRepositoryFindByIdAsyncReturnsNull_
147147

148148
[Fact]
149149
[Trait("Category", "Unit")]
150-
public async Task GivenRetrieveByIdAsync_WhenRepositoryFindByIdAsyncReturnsPlayer_TheResultShouldBePlayer()
150+
public async Task RetrieveByIdAsync_PlayerFound_ReturnsMappedResponseModel()
151151
{
152152
// Arrange
153153
var id = Guid.NewGuid();
@@ -178,7 +178,7 @@ public async Task GivenRetrieveByIdAsync_WhenRepositoryFindByIdAsyncReturnsPlaye
178178

179179
[Fact]
180180
[Trait("Category", "Unit")]
181-
public async Task GivenRetrieveBySquadNumberAsync_WhenRepositoryFindBySquadNumberAsyncReturnsNull_ThenResultShouldBeNull()
181+
public async Task RetrieveBySquadNumberAsync_PlayerNotFound_ReturnsNull()
182182
{
183183
// Arrange
184184
var squadNumber = 999;
@@ -209,7 +209,7 @@ public async Task GivenRetrieveBySquadNumberAsync_WhenRepositoryFindBySquadNumbe
209209

210210
[Fact]
211211
[Trait("Category", "Unit")]
212-
public async Task GivenRetrieveBySquadNumberAsync_WhenRepositoryFindBySquadNumberAsyncReturnsPlayer_ThenResultShouldBePlayer()
212+
public async Task RetrieveBySquadNumberAsync_PlayerFound_ReturnsMappedResponseModel()
213213
{
214214
// Arrange
215215
var squadNumber = 10;
@@ -248,7 +248,7 @@ public async Task GivenRetrieveBySquadNumberAsync_WhenRepositoryFindBySquadNumbe
248248

249249
[Fact]
250250
[Trait("Category", "Unit")]
251-
public async Task GivenUpdateAsync_WhenRepositoryFindBySquadNumberAsyncReturnsPlayer_ThenRepositoryUpdateAsyncAndCacheRemove()
251+
public async Task UpdateAsync_PlayerFound_UpdatesRepositoryAndRemovesCache()
252252
{
253253
// Arrange
254254
var squadNumber = 23;
@@ -283,13 +283,50 @@ public async Task GivenUpdateAsync_WhenRepositoryFindBySquadNumberAsyncReturnsPl
283283
);
284284
}
285285

286+
[Fact]
287+
[Trait("Category", "Unit")]
288+
public async Task UpdateAsync_PlayerNotFound_DoesNotUpdateRepository()
289+
{
290+
// Arrange
291+
var squadNumber = 999;
292+
var request = PlayerFakes.MakeRequestModelForCreate();
293+
request.SquadNumber = squadNumber;
294+
var (repository, logger, memoryCache, mapper, environment) = PlayerMocks.InitServiceMocks();
295+
repository
296+
.Setup(repository => repository.FindBySquadNumberAsync(squadNumber))
297+
.ReturnsAsync(null as Player);
298+
299+
var service = new PlayerService(
300+
repository.Object,
301+
logger.Object,
302+
memoryCache.Object,
303+
mapper.Object,
304+
environment.Object
305+
);
306+
307+
// Act
308+
await service.UpdateAsync(request);
309+
310+
// Assert
311+
repository.Verify(
312+
repository => repository.FindBySquadNumberAsync(It.IsAny<int>()),
313+
Times.Once
314+
);
315+
repository.Verify(repository => repository.UpdateAsync(It.IsAny<Player>()), Times.Never);
316+
memoryCache.Verify(cache => cache.Remove(It.IsAny<object>()), Times.Never);
317+
mapper.Verify(
318+
mapper => mapper.Map(It.IsAny<PlayerRequestModel>(), It.IsAny<Player>()),
319+
Times.Never
320+
);
321+
}
322+
286323
/* -------------------------------------------------------------------------
287324
* Delete
288325
* ---------------------------------------------------------------------- */
289326

290327
[Fact]
291328
[Trait("Category", "Unit")]
292-
public async Task GivenDeleteAsync_WhenRepositoryFindBySquadNumberAsyncReturnsPlayer_ThenRepositoryDeleteAsyncAndCacheRemove()
329+
public async Task DeleteAsync_PlayerFound_RemovesFromRepositoryAndRemovesCache()
293330
{
294331
// Arrange
295332
var squadNumber = 26;
@@ -319,6 +356,37 @@ public async Task GivenDeleteAsync_WhenRepositoryFindBySquadNumberAsyncReturnsPl
319356
memoryCache.Verify(cache => cache.Remove(It.IsAny<object>()), Times.Once);
320357
}
321358

359+
[Fact]
360+
[Trait("Category", "Unit")]
361+
public async Task DeleteAsync_PlayerNotFound_DoesNotRemoveFromRepository()
362+
{
363+
// Arrange
364+
var squadNumber = 999;
365+
var (repository, logger, memoryCache, mapper, environment) = PlayerMocks.InitServiceMocks();
366+
repository
367+
.Setup(repository => repository.FindBySquadNumberAsync(squadNumber))
368+
.ReturnsAsync(null as Player);
369+
370+
var service = new PlayerService(
371+
repository.Object,
372+
logger.Object,
373+
memoryCache.Object,
374+
mapper.Object,
375+
environment.Object
376+
);
377+
378+
// Act
379+
await service.DeleteAsync(squadNumber);
380+
381+
// Assert
382+
repository.Verify(
383+
repository => repository.FindBySquadNumberAsync(It.IsAny<int>()),
384+
Times.Once
385+
);
386+
repository.Verify(repository => repository.RemoveAsync(It.IsAny<Guid>()), Times.Never);
387+
memoryCache.Verify(cache => cache.Remove(It.IsAny<object>()), Times.Never);
388+
}
389+
322390
private static async Task<long> ExecutionTimeAsync(Func<Task> awaitable)
323391
{
324392
var stopwatch = new Stopwatch();

0 commit comments

Comments
 (0)