Skip to content

Commit 71b10d7

Browse files
nanotaboadaCopilot
andcommitted
test: fix flaky and silent assertions in unit tests (#396)
- Replace if-guarded 201 assertion with unconditional BeOfType and add RouteValues squadNumber value check (PlayerControllerTests) - Inject TimeProvider into PlayerRequestModelValidator to eliminate DateTime.UtcNow coupling - Add FakeTimeProvider in PlayerValidatorTests and pin DateOfBirthToday to a fixed clock shared with the validator Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 18a5c59 commit 71b10d7

3 files changed

Lines changed: 24 additions & 14 deletions

File tree

src/Dotnet.Samples.AspNetCore.WebApi/Validators/PlayerRequestModelValidator.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ public class PlayerRequestModelValidator : AbstractValidator<PlayerRequestModel>
1818
{
1919
private readonly IPlayerRepository _playerRepository;
2020

21-
public PlayerRequestModelValidator(IPlayerRepository playerRepository)
21+
public PlayerRequestModelValidator(
22+
IPlayerRepository playerRepository,
23+
TimeProvider? timeProvider = null
24+
)
2225
{
2326
_playerRepository = playerRepository;
27+
var clock = timeProvider ?? TimeProvider.System;
2428

2529
RuleFor(player => player.FirstName).NotEmpty().WithMessage("FirstName is required.");
2630

@@ -45,7 +49,7 @@ public PlayerRequestModelValidator(IPlayerRepository playerRepository)
4549
() =>
4650
{
4751
RuleFor(player => player.DateOfBirth)
48-
.Must(date => date!.Value.Date < DateTime.UtcNow.Date)
52+
.Must(date => date!.Value.Date < clock.GetUtcNow().Date)
4953
.WithMessage("DateOfBirth must be a date in the past.")
5054
.Must(date =>
5155
date!.Value.Date >= new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,12 @@ public async Task Post_Players_NonExisting_Returns201Created()
146146
),
147147
Times.Once
148148
);
149-
if (result is CreatedAtRoute<PlayerResponseModel> httpResult)
150-
{
151-
httpResult.Should().NotBeNull().And.BeOfType<CreatedAtRoute<PlayerResponseModel>>();
152-
httpResult.StatusCode.Should().Be(StatusCodes.Status201Created);
153-
httpResult.Value.Should().BeEquivalentTo(response);
154-
httpResult.RouteName.Should().Be("RetrieveBySquadNumber");
155-
httpResult.RouteValues.Should().NotBeNull().And.ContainKey("squadNumber");
156-
}
149+
var httpResult = result.Should().BeOfType<CreatedAtRoute<PlayerResponseModel>>().Subject;
150+
httpResult.StatusCode.Should().Be(StatusCodes.Status201Created);
151+
httpResult.Value.Should().BeEquivalentTo(response);
152+
httpResult.RouteName.Should().Be("RetrieveBySquadNumber");
153+
httpResult.RouteValues.Should().NotBeNull().And.ContainKey("squadNumber");
154+
httpResult.RouteValues!["squadNumber"].Should().Be(response.Dorsal);
157155
}
158156

159157
/* -------------------------------------------------------------------------

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@ namespace Dotnet.Samples.AspNetCore.WebApi.Tests.Unit;
99

1010
public class PlayerValidatorTests
1111
{
12+
private sealed class FakeTimeProvider(DateTimeOffset utcNow) : TimeProvider
13+
{
14+
public override DateTimeOffset GetUtcNow() => utcNow;
15+
}
16+
1217
private static PlayerRequestModelValidator CreateValidator(
13-
Mock<IPlayerRepository>? repositoryMock = null
18+
Mock<IPlayerRepository>? repositoryMock = null,
19+
TimeProvider? timeProvider = null
1420
)
1521
{
1622
var mock = repositoryMock ?? new Mock<IPlayerRepository>();
17-
return new PlayerRequestModelValidator(mock.Object);
23+
return new PlayerRequestModelValidator(mock.Object, timeProvider);
1824
}
1925

2026
/* -------------------------------------------------------------------------
@@ -213,9 +219,11 @@ public async Task ValidateAsync_DateOfBirthInFuture_ReturnsValidationError()
213219
public async Task ValidateAsync_DateOfBirthToday_ReturnsValidationError()
214220
{
215221
// Arrange
222+
var fixedNow = new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero);
223+
var timeProvider = new FakeTimeProvider(fixedNow);
216224
var request = PlayerFakes.MakeRequestModelForCreate();
217-
request.DateOfBirth = DateTime.UtcNow.Date; // rule: strictly < UtcNow.Date
218-
var validator = CreateValidator();
225+
request.DateOfBirth = fixedNow.Date; // same "today" the validator sees
226+
var validator = CreateValidator(timeProvider: timeProvider);
219227

220228
// Act
221229
var result = await validator.ValidateAsync(request);

0 commit comments

Comments
 (0)