Skip to content

Commit 10456dc

Browse files
committed
refactor: replace FluentAssertions with Shouldly in tests
- Migrated all test assertions from FluentAssertions to Shouldly - Removed all FluentAssertions package references - Updated README to reflect use of Shouldly
1 parent 7448626 commit 10456dc

16 files changed

+69
-68
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" />
3232
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
3333
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
34-
<PackageVersion Include="FluentAssertions" Version="6.12.2" />
3534
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
3635
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
3736
<PackageVersion Include="MediatR" Version="12.4.1" />
@@ -49,6 +48,7 @@
4948
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="$(EfcoreVersion)" />
5049
<!--#if (UsePostgreSQL)-->
5150
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
51+
<PackageVersion Include="Shouldly" Version="4.3.0" />
5252
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.0.0" />
5353
<!--#endif-->
5454
<!--#if (UseSqlite)-->

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ azd up
107107
* [MediatR](https://github.com/jbogard/MediatR)
108108
* [AutoMapper](https://automapper.org/)
109109
* [FluentValidation](https://fluentvalidation.net/)
110-
* [NUnit](https://nunit.org/), [FluentAssertions](https://fluentassertions.com/), [Moq](https://github.com/devlooped/moq) & [Respawn](https://github.com/jbogard/Respawn)
110+
* [NUnit](https://nunit.org/), [Shoudly](https://docs.shouldly.org/), [Moq](https://github.com/devlooped/moq) & [Respawn](https://github.com/jbogard/Respawn)
111111

112112
## Versions
113113
The main branch is now on .NET 9.0. The following previous versions are available:

tests/Application.FunctionalTests/Application.FunctionalTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
<PrivateAssets>all</PrivateAssets>
3333
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3434
</PackageReference>
35-
<PackageReference Include="FluentAssertions" />
3635
<PackageReference Include="Moq" />
3736
<PackageReference Include="Respawn" />
37+
<PackageReference Include="Shouldly" />
3838
<PackageReference Include="System.Configuration.ConfigurationManager" />
3939
<!--#if (UsePostgreSQL)-->
4040
<PackageReference Include="Testcontainers.PostgreSql" />
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
global using Ardalis.GuardClauses;
2-
global using FluentAssertions;
2+
global using Shouldly;
33
global using Moq;
4-
global using NUnit.Framework;
4+
global using NUnit.Framework;

tests/Application.FunctionalTests/TodoLists/Commands/CreateTodoListTests.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class CreateTodoListTests : BaseTestFixture
1212
public async Task ShouldRequireMinimumFields()
1313
{
1414
var command = new CreateTodoListCommand();
15-
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<ValidationException>();
15+
await Should.ThrowAsync<ValidationException>(() => SendAsync(command));
1616
}
1717

1818
[Test]
@@ -28,8 +28,7 @@ await SendAsync(new CreateTodoListCommand
2828
Title = "Shopping"
2929
};
3030

31-
await FluentActions.Invoking(() =>
32-
SendAsync(command)).Should().ThrowAsync<ValidationException>();
31+
await Should.ThrowAsync<ValidationException>(() => SendAsync(command));
3332
}
3433

3534
[Test]
@@ -46,9 +45,9 @@ public async Task ShouldCreateTodoList()
4645

4746
var list = await FindAsync<TodoList>(id);
4847

49-
list.Should().NotBeNull();
50-
list!.Title.Should().Be(command.Title);
51-
list.CreatedBy.Should().Be(userId);
52-
list.Created.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
48+
list.ShouldNotBeNull();
49+
list!.Title.ShouldBe(command.Title);
50+
list.CreatedBy.ShouldBe(userId);
51+
list.Created.ShouldBe(DateTime.Now, TimeSpan.FromMilliseconds(10000));
5352
}
5453
}

tests/Application.FunctionalTests/TodoLists/Commands/DeleteTodoListTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class DeleteTodoListTests : BaseTestFixture
1212
public async Task ShouldRequireValidTodoListId()
1313
{
1414
var command = new DeleteTodoListCommand(99);
15-
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<NotFoundException>();
15+
await Should.ThrowAsync<NotFoundException>(() => SendAsync(command));
1616
}
1717

1818
[Test]
@@ -27,6 +27,6 @@ public async Task ShouldDeleteTodoList()
2727

2828
var list = await FindAsync<TodoList>(listId);
2929

30-
list.Should().BeNull();
30+
list.ShouldBeNull();
3131
}
3232
}

tests/Application.FunctionalTests/TodoLists/Commands/PurgeTodoListsTests.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ public async Task ShouldDenyAnonymousUser()
1515
{
1616
var command = new PurgeTodoListsCommand();
1717

18-
command.GetType().Should().BeDecoratedWith<AuthorizeAttribute>();
18+
command.GetType().ShouldSatisfyAllConditions(
19+
type => type.ShouldBeDecoratedWith<AuthorizeAttribute>()
20+
);
1921

2022
var action = () => SendAsync(command);
2123

22-
await action.Should().ThrowAsync<UnauthorizedAccessException>();
24+
await Should.ThrowAsync<UnauthorizedAccessException>(action);
2325
}
2426

2527
[Test]
@@ -31,7 +33,7 @@ public async Task ShouldDenyNonAdministrator()
3133

3234
var action = () => SendAsync(command);
3335

34-
await action.Should().ThrowAsync<ForbiddenAccessException>();
36+
await Should.ThrowAsync<ForbiddenAccessException>(action);
3537
}
3638

3739
[Test]
@@ -43,7 +45,8 @@ public async Task ShouldAllowAdministrator()
4345

4446
var action = () => SendAsync(command);
4547

46-
await action.Should().NotThrowAsync<ForbiddenAccessException>();
48+
Func<Task> asyncAction = async () => await SendAsync(command);
49+
await asyncAction.ShouldNotThrowAsync();
4750
}
4851

4952
[Test]
@@ -70,6 +73,6 @@ await SendAsync(new CreateTodoListCommand
7073

7174
var count = await CountAsync<TodoList>();
7275

73-
count.Should().Be(0);
76+
count.ShouldBe(0);
7477
}
7578
}

tests/Application.FunctionalTests/TodoLists/Commands/UpdateTodoListTests.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class UpdateTodoListTests : BaseTestFixture
1313
public async Task ShouldRequireValidTodoListId()
1414
{
1515
var command = new UpdateTodoListCommand { Id = 99, Title = "New Title" };
16-
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<NotFoundException>();
16+
await Should.ThrowAsync<NotFoundException>(() => SendAsync(command));
1717
}
1818

1919
[Test]
@@ -35,10 +35,10 @@ await SendAsync(new CreateTodoListCommand
3535
Title = "Other List"
3636
};
3737

38-
(await FluentActions.Invoking(() =>
39-
SendAsync(command))
40-
.Should().ThrowAsync<ValidationException>().Where(ex => ex.Errors.ContainsKey("Title")))
41-
.And.Errors["Title"].Should().Contain("'Title' must be unique.");
38+
var ex = await Should.ThrowAsync<ValidationException>(() => SendAsync(command));
39+
40+
ex.Errors.ShouldContainKey("Title");
41+
ex.Errors["Title"].ShouldContain("'Title' must be unique.");
4242
}
4343

4444
[Test]
@@ -61,10 +61,10 @@ public async Task ShouldUpdateTodoList()
6161

6262
var list = await FindAsync<TodoList>(listId);
6363

64-
list.Should().NotBeNull();
65-
list!.Title.Should().Be(command.Title);
66-
list.LastModifiedBy.Should().NotBeNull();
67-
list.LastModifiedBy.Should().Be(userId);
68-
list.LastModified.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
64+
list.ShouldNotBeNull();
65+
list!.Title.ShouldBe(command.Title);
66+
list.LastModifiedBy.ShouldNotBeNull();
67+
list.LastModifiedBy.ShouldBe(userId);
68+
list.LastModified.ShouldBe(DateTime.Now, TimeSpan.FromMilliseconds(10000));
6969
}
7070
}

tests/Application.FunctionalTests/TodoLists/Queries/GetTodosTests.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public async Task ShouldReturnPriorityLevels()
1717

1818
var result = await SendAsync(query);
1919

20-
result.PriorityLevels.Should().NotBeEmpty();
20+
result.PriorityLevels.ShouldNotBeEmpty();
2121
}
2222

2323
[Test]
@@ -30,23 +30,23 @@ await AddAsync(new TodoList
3030
Title = "Shopping",
3131
Colour = Colour.Blue,
3232
Items =
33-
{
34-
new TodoItem { Title = "Apples", Done = true },
35-
new TodoItem { Title = "Milk", Done = true },
36-
new TodoItem { Title = "Bread", Done = true },
37-
new TodoItem { Title = "Toilet paper" },
38-
new TodoItem { Title = "Pasta" },
39-
new TodoItem { Title = "Tissues" },
40-
new TodoItem { Title = "Tuna" }
41-
}
33+
{
34+
new TodoItem { Title = "Apples", Done = true },
35+
new TodoItem { Title = "Milk", Done = true },
36+
new TodoItem { Title = "Bread", Done = true },
37+
new TodoItem { Title = "Toilet paper" },
38+
new TodoItem { Title = "Pasta" },
39+
new TodoItem { Title = "Tissues" },
40+
new TodoItem { Title = "Tuna" }
41+
}
4242
});
4343

4444
var query = new GetTodosQuery();
4545

4646
var result = await SendAsync(query);
4747

48-
result.Lists.Should().HaveCount(1);
49-
result.Lists.First().Items.Should().HaveCount(7);
48+
result.Lists.Count.ShouldBe(1);
49+
result.Lists.First().Items.Count.ShouldBe(7);
5050
}
5151

5252
[Test]
@@ -55,7 +55,7 @@ public async Task ShouldDenyAnonymousUser()
5555
var query = new GetTodosQuery();
5656

5757
var action = () => SendAsync(query);
58-
59-
await action.Should().ThrowAsync<UnauthorizedAccessException>();
58+
59+
await Should.ThrowAsync<UnauthorizedAccessException>(action);
6060
}
6161
}

tests/Application.UnitTests/Application.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
<PrivateAssets>all</PrivateAssets>
1919
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2020
</PackageReference>
21-
<PackageReference Include="FluentAssertions" />
2221
<PackageReference Include="Moq" />
22+
<PackageReference Include="Shouldly" />
2323
</ItemGroup>
2424

2525
<ItemGroup>

tests/Application.UnitTests/Common/Exceptions/ValidationExceptionTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using CleanArchitecture.Application.Common.Exceptions;
2-
using FluentAssertions;
32
using FluentValidation.Results;
43
using NUnit.Framework;
4+
using Shouldly;
55

66
namespace CleanArchitecture.Application.UnitTests.Common.Exceptions;
77

@@ -12,7 +12,7 @@ public void DefaultConstructorCreatesAnEmptyErrorDictionary()
1212
{
1313
var actual = new ValidationException().Errors;
1414

15-
actual.Keys.Should().BeEquivalentTo(Array.Empty<string>());
15+
actual.Keys.ShouldBeEmpty();
1616
}
1717

1818
[Test]
@@ -25,8 +25,8 @@ public void SingleValidationFailureCreatesASingleElementErrorDictionary()
2525

2626
var actual = new ValidationException(failures).Errors;
2727

28-
actual.Keys.Should().BeEquivalentTo(new string[] { "Age" });
29-
actual["Age"].Should().BeEquivalentTo(new string[] { "must be over 18" });
28+
actual.Keys.ShouldBe(new string[] { "Age" });
29+
actual["Age"].ShouldBe(new string[] { "must be over 18" });
3030
}
3131

3232
[Test]
@@ -44,20 +44,20 @@ public void MulitpleValidationFailureForMultiplePropertiesCreatesAMultipleElemen
4444

4545
var actual = new ValidationException(failures).Errors;
4646

47-
actual.Keys.Should().BeEquivalentTo(new string[] { "Password", "Age" });
47+
actual.Keys.ShouldBe(new string[] { "Password", "Age" }, ignoreOrder: true);
4848

49-
actual["Age"].Should().BeEquivalentTo(new string[]
49+
actual["Age"].ShouldBe(new string[]
5050
{
5151
"must be 25 or younger",
5252
"must be 18 or older",
53-
});
53+
}, ignoreOrder: true);
5454

55-
actual["Password"].Should().BeEquivalentTo(new string[]
55+
actual["Password"].ShouldBe(new string[]
5656
{
5757
"must contain lower case letter",
5858
"must contain upper case letter",
5959
"must contain at least 8 characters",
6060
"must contain a digit",
61-
});
61+
}, ignoreOrder: true);
6262
}
6363
}

tests/Domain.UnitTests/Domain.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<PrivateAssets>all</PrivateAssets>
1818
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1919
</PackageReference>
20-
<PackageReference Include="FluentAssertions" />
20+
<PackageReference Include="Shouldly" />
2121
</ItemGroup>
2222

2323
<ItemGroup>
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using CleanArchitecture.Domain.Exceptions;
22
using CleanArchitecture.Domain.ValueObjects;
3-
using FluentAssertions;
43
using NUnit.Framework;
4+
using Shouldly;
55

66
namespace CleanArchitecture.Domain.UnitTests.ValueObjects;
77

@@ -14,37 +14,36 @@ public void ShouldReturnCorrectColourCode()
1414

1515
var colour = Colour.From(code);
1616

17-
colour.Code.Should().Be(code);
17+
colour.Code.ShouldBe(code);
1818
}
1919

2020
[Test]
2121
public void ToStringReturnsCode()
2222
{
2323
var colour = Colour.White;
2424

25-
colour.ToString().Should().Be(colour.Code);
25+
colour.ToString().ShouldBe(colour.Code);
2626
}
2727

2828
[Test]
2929
public void ShouldPerformImplicitConversionToColourCodeString()
3030
{
3131
string code = Colour.White;
3232

33-
code.Should().Be("#FFFFFF");
33+
code.ShouldBe("#FFFFFF");
3434
}
3535

3636
[Test]
3737
public void ShouldPerformExplicitConversionGivenSupportedColourCode()
3838
{
3939
var colour = (Colour)"#FFFFFF";
4040

41-
colour.Should().Be(Colour.White);
41+
colour.ShouldBe(Colour.White);
4242
}
4343

4444
[Test]
4545
public void ShouldThrowUnsupportedColourExceptionGivenNotSupportedColourCode()
4646
{
47-
FluentActions.Invoking(() => Colour.From("##FF33CC"))
48-
.Should().Throw<UnsupportedColourException>();
47+
Should.Throw<UnsupportedColourException>(() => Colour.From("##FF33CC"));
4948
}
5049
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
global using CleanArchitecture.Web.AcceptanceTests.Pages;
22
global using BoDi;
3-
global using FluentAssertions;
3+
global using Shouldly;
44
global using Microsoft.Playwright;
5-
global using TechTalk.SpecFlow;
5+
global using TechTalk.SpecFlow;

tests/Web.AcceptanceTests/StepDefinitions/LoginStepDefinitions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ public async Task TheyLogInSuccessfully()
5252
{
5353
var profileLinkText = await _loginPage.ProfileLinkText();
5454

55-
profileLinkText.Should().NotBeNull();
56-
profileLinkText.Should().Be("Account");
55+
profileLinkText.ShouldNotBeNull();
56+
profileLinkText.ShouldBe("Account");
5757
}
5858

5959
[When("the user logs in with invalid credentials")]
@@ -69,7 +69,7 @@ public async Task AnErrorIsDisplayed()
6969
{
7070
var errorVisible = await _loginPage.InvalidLoginAttemptMessageVisible();
7171

72-
errorVisible.Should().BeTrue();
72+
errorVisible.ShouldBeTrue();
7373
}
7474

7575
[AfterFeature]
@@ -81,4 +81,4 @@ public static async Task AfterScenario(IObjectContainer container)
8181
await browser.CloseAsync();
8282
playright.Dispose();
8383
}
84-
}
84+
}

0 commit comments

Comments
 (0)