From 39aba7f38a2a550d8d82082437ad905091110a1e Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Mon, 18 Nov 2024 06:44:34 +1000
Subject: [PATCH 01/10] Converted Domain tests to TUnit
---
tests/Directory.Packages.props | 1 +
.../Domain.UnitTests/Domain.UnitTests.csproj | 13 +++++-----
tests/Domain.UnitTests/Heroes/HeroTests.cs | 26 +++++++++----------
tests/Domain.UnitTests/Heroes/PowerTests.cs | 18 ++++++-------
tests/Domain.UnitTests/Teams/MissionTests.cs | 6 ++---
tests/Domain.UnitTests/Teams/TeamTests.cs | 24 ++++++++---------
tests/Domain.UnitTests/Usings.cs | 2 +-
7 files changed, 46 insertions(+), 44 deletions(-)
diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props
index 77891684..10415c45 100644
--- a/tests/Directory.Packages.props
+++ b/tests/Directory.Packages.props
@@ -7,6 +7,7 @@
+
all
diff --git a/tests/Domain.UnitTests/Domain.UnitTests.csproj b/tests/Domain.UnitTests/Domain.UnitTests.csproj
index 21fc7ab2..79890f86 100644
--- a/tests/Domain.UnitTests/Domain.UnitTests.csproj
+++ b/tests/Domain.UnitTests/Domain.UnitTests.csproj
@@ -12,12 +12,13 @@
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
+
+
+
+
+
+
+
diff --git a/tests/Domain.UnitTests/Heroes/HeroTests.cs b/tests/Domain.UnitTests/Heroes/HeroTests.cs
index 64f69bc6..29634f88 100644
--- a/tests/Domain.UnitTests/Heroes/HeroTests.cs
+++ b/tests/Domain.UnitTests/Heroes/HeroTests.cs
@@ -4,9 +4,9 @@ namespace SSW.CleanArchitecture.Domain.UnitTests.Heroes;
public class HeroTests
{
- [Theory]
- [InlineData("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
- [InlineData("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
+ [Test]
+ [Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
+ [Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
public void HeroId_ShouldBeComparable(string stringGuid1, string stringGuid2, bool isEqual)
{
// Arrange
@@ -24,7 +24,7 @@ public void HeroId_ShouldBeComparable(string stringGuid1, string stringGuid2, bo
id2.Value.Should().Be(guid2);
}
- [Fact]
+ [Test]
public void Create_WithValidNameAndAlias_ShouldSucceed()
{
// Arrange
@@ -40,7 +40,7 @@ public void Create_WithValidNameAndAlias_ShouldSucceed()
hero.Alias.Should().Be(alias);
}
- [Fact]
+ [Test]
public void Create_WithSameNameAndAlias_ShouldSucceed()
{
// Arrange
@@ -51,10 +51,10 @@ public void Create_WithSameNameAndAlias_ShouldSucceed()
Hero.Create(name, alias);
}
- [Theory]
- [InlineData(null, "alias")]
- [InlineData("name", null)]
- [InlineData(null, null)]
+ [Test]
+ [Arguments(null, "alias")]
+ [Arguments("name", null)]
+ [Arguments(null, null)]
public void Create_WithNullTitleOrAlias_ShouldThrow(string? name, string? alias)
{
// Arrange
@@ -66,7 +66,7 @@ public void Create_WithNullTitleOrAlias_ShouldThrow(string? name, string? alias)
act.Should().Throw().WithMessage("Value cannot be null*");
}
- [Fact]
+ [Test]
public void AddPower_ShouldUpdateHeroPowerLevel()
{
// Act
@@ -79,7 +79,7 @@ public void AddPower_ShouldUpdateHeroPowerLevel()
hero.Powers.Should().HaveCount(2);
}
- [Fact]
+ [Test]
public void RemovePower_ShouldUpdateHeroPowerLevel()
{
// Act
@@ -95,7 +95,7 @@ public void RemovePower_ShouldUpdateHeroPowerLevel()
hero.Powers.Should().HaveCount(1);
}
- [Fact]
+ [Test]
public void AddPower_ShouldRaisePowerLevelUpdatedEvent()
{
// Act
@@ -117,7 +117,7 @@ public void AddPower_ShouldRaisePowerLevelUpdatedEvent()
hero.Powers.Should().ContainSingle("Super-strength");
}
- [Fact]
+ [Test]
public void RemovePower_ShouldRaisePowerLevelUpdatedEvent()
{
// Act
diff --git a/tests/Domain.UnitTests/Heroes/PowerTests.cs b/tests/Domain.UnitTests/Heroes/PowerTests.cs
index be5adcd8..8a927327 100644
--- a/tests/Domain.UnitTests/Heroes/PowerTests.cs
+++ b/tests/Domain.UnitTests/Heroes/PowerTests.cs
@@ -4,7 +4,7 @@ namespace SSW.CleanArchitecture.Domain.UnitTests.Heroes;
public class PowerTests
{
- [Fact]
+ [Test]
public void Power_ShouldBeCreatable()
{
// Arrange
@@ -20,7 +20,7 @@ public void Power_ShouldBeCreatable()
power.PowerLevel.Should().Be(powerLevel);
}
- [Fact]
+ [Test]
public void Power_ShouldBeComparable()
{
// Arrange
@@ -36,13 +36,13 @@ public void Power_ShouldBeComparable()
areEqual.Should().BeTrue();
}
- [Theory]
- [InlineData(-1, true)]
- [InlineData(0, true)]
- [InlineData(1, false)]
- [InlineData(9, false)]
- [InlineData(10, false)]
- [InlineData(11, true)]
+ [Test]
+ [Arguments(-1, true)]
+ [Arguments(0, true)]
+ [Arguments(1, false)]
+ [Arguments(9, false)]
+ [Arguments(10, false)]
+ [Arguments(11, true)]
public void Power_WithInvalidPowerLevel_ShouldThrow(int powerLevel, bool shouldThrow)
{
// Arrange
diff --git a/tests/Domain.UnitTests/Teams/MissionTests.cs b/tests/Domain.UnitTests/Teams/MissionTests.cs
index c9211bee..05be821d 100644
--- a/tests/Domain.UnitTests/Teams/MissionTests.cs
+++ b/tests/Domain.UnitTests/Teams/MissionTests.cs
@@ -4,9 +4,9 @@ namespace SSW.CleanArchitecture.Domain.UnitTests.Teams;
public class MissionTests
{
- [Theory]
- [InlineData("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
- [InlineData("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
+ [Test]
+ [Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
+ [Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
public void MissionId_ShouldBeComparable(string stringGuid1, string stringGuid2, bool isEqual)
{
// Arrange
diff --git a/tests/Domain.UnitTests/Teams/TeamTests.cs b/tests/Domain.UnitTests/Teams/TeamTests.cs
index 77134d2f..9bc0dabc 100644
--- a/tests/Domain.UnitTests/Teams/TeamTests.cs
+++ b/tests/Domain.UnitTests/Teams/TeamTests.cs
@@ -5,9 +5,9 @@ namespace SSW.CleanArchitecture.Domain.UnitTests.Teams;
public class TeamTests
{
- [Theory]
- [InlineData("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
- [InlineData("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
+ [Test]
+ [Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
+ [Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
public void TeamId_ShouldBeComparable(string stringGuid1, string stringGuid2, bool isEqual)
{
// Arrange
@@ -25,7 +25,7 @@ public void TeamId_ShouldBeComparable(string stringGuid1, string stringGuid2, bo
id2.Value.Should().Be(guid2);
}
- [Fact]
+ [Test]
public void Create_WithValidNameAndAlias_ShouldSucceed()
{
// Arrange
@@ -39,7 +39,7 @@ public void Create_WithValidNameAndAlias_ShouldSucceed()
team.Name.Should().Be(name);
}
- [Fact]
+ [Test]
public void Create_WithNullNameAndAlias_ShouldThrow()
{
// Arrange
@@ -52,7 +52,7 @@ public void Create_WithNullNameAndAlias_ShouldThrow()
act.Should().Throw().WithMessage("Value cannot be null. (Parameter 'name')");
}
- [Fact]
+ [Test]
public void AddHero_ShouldUpdateTeamPowerLevel()
{
// Arrange
@@ -72,7 +72,7 @@ public void AddHero_ShouldUpdateTeamPowerLevel()
team.TotalPowerLevel.Should().Be(14);
}
- [Fact]
+ [Test]
public void RemoveHero_ShouldUpdateTeamPowerLevel()
{
// Arrange
@@ -93,7 +93,7 @@ public void RemoveHero_ShouldUpdateTeamPowerLevel()
team.TotalPowerLevel.Should().Be(4);
}
- [Fact]
+ [Test]
public void ExecuteMission_ShouldUpdateTeamStatus()
{
// Arrange
@@ -108,7 +108,7 @@ public void ExecuteMission_ShouldUpdateTeamStatus()
team.Missions.Should().ContainSingle(x => x.Description == "Mission");
}
- [Fact]
+ [Test]
public void ExecuteMission_WhenTeamNotAvailable_ShouldError()
{
// Arrange
@@ -123,7 +123,7 @@ public void ExecuteMission_WhenTeamNotAvailable_ShouldError()
result.FirstError.Should().Be(TeamErrors.NotAvailable);
}
- [Fact]
+ [Test]
public void CompleteCurrentMission_ShouldUpdateTeamStatus()
{
// Arrange
@@ -137,7 +137,7 @@ public void CompleteCurrentMission_ShouldUpdateTeamStatus()
team.Status.Should().Be(TeamStatus.Available);
}
- [Fact]
+ [Test]
public void CompleteCurrentMission_WhenNoMissionHasBeenExecuted_ShouldThrow()
{
// Arrange
@@ -151,7 +151,7 @@ public void CompleteCurrentMission_WhenNoMissionHasBeenExecuted_ShouldThrow()
result.FirstError.Should().Be(TeamErrors.NotOnMission);
}
- [Fact]
+ [Test]
public void CompleteCurrentMission_WhenNotOnMission_ShouldError()
{
// Arrange
diff --git a/tests/Domain.UnitTests/Usings.cs b/tests/Domain.UnitTests/Usings.cs
index 7fef4b0e..62b52c3a 100644
--- a/tests/Domain.UnitTests/Usings.cs
+++ b/tests/Domain.UnitTests/Usings.cs
@@ -1,2 +1,2 @@
-global using Xunit;
+global using TUnit;
global using FluentAssertions;
\ No newline at end of file
From 71dfe4e8091ea0f4639d11a9da8bb6035a855807 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Mon, 18 Nov 2024 06:52:20 +1000
Subject: [PATCH 02/10] Converted arch tests to TUnit
---
tests/Architecture.Tests/Application.cs | 4 +--
.../Architecture.Tests.csproj | 14 +++++-----
.../Common/TestResultAssertions.cs | 1 +
.../Common/TestResultExtensions.cs | 27 ++++++++++---------
.../Common/TypeExtensions.cs | 25 ++++++++---------
tests/Architecture.Tests/DomainTests.cs | 4 +--
tests/Architecture.Tests/Layer.cs | 8 +++---
tests/Architecture.Tests/Presentation.cs | 2 +-
tests/Architecture.Tests/Usings.cs | 2 +-
9 files changed, 46 insertions(+), 41 deletions(-)
diff --git a/tests/Architecture.Tests/Application.cs b/tests/Architecture.Tests/Application.cs
index 0cc8f2ac..dbcdcfbe 100644
--- a/tests/Architecture.Tests/Application.cs
+++ b/tests/Architecture.Tests/Application.cs
@@ -7,7 +7,7 @@ public class Application : TestBase
{
private static readonly Type IRequestHandler = typeof(IRequestHandler<,>);
- [Fact]
+ [Test]
public void CommandHandlers_ShouldHaveCorrectSuffix()
{
var types = Types
@@ -25,7 +25,7 @@ public void CommandHandlers_ShouldHaveCorrectSuffix()
result.Should().BeSuccessful();
}
- [Fact]
+ [Test]
public void QueryHandlers_ShouldHaveCorrectSuffix()
{
var types = Types
diff --git a/tests/Architecture.Tests/Architecture.Tests.csproj b/tests/Architecture.Tests/Architecture.Tests.csproj
index 0f85732f..dcc14643 100644
--- a/tests/Architecture.Tests/Architecture.Tests.csproj
+++ b/tests/Architecture.Tests/Architecture.Tests.csproj
@@ -12,13 +12,15 @@
-
+
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
+
+
+
+
+
+
+
diff --git a/tests/Architecture.Tests/Common/TestResultAssertions.cs b/tests/Architecture.Tests/Common/TestResultAssertions.cs
index c49956d7..3b938ff7 100644
--- a/tests/Architecture.Tests/Common/TestResultAssertions.cs
+++ b/tests/Architecture.Tests/Common/TestResultAssertions.cs
@@ -1,6 +1,7 @@
using FluentAssertions.Execution;
using FluentAssertions.Primitives;
using System.Text;
+using TestResult = NetArchTest.Rules.TestResult;
namespace SSW.CleanArchitecture.Architecture.UnitTests.Common;
diff --git a/tests/Architecture.Tests/Common/TestResultExtensions.cs b/tests/Architecture.Tests/Common/TestResultExtensions.cs
index 767fa0f6..3432826c 100644
--- a/tests/Architecture.Tests/Common/TestResultExtensions.cs
+++ b/tests/Architecture.Tests/Common/TestResultExtensions.cs
@@ -1,19 +1,20 @@
-using Xunit.Abstractions;
+// using Xunit.Abstractions;
+using TestResult = NetArchTest.Rules.TestResult;
namespace SSW.CleanArchitecture.Architecture.UnitTests.Common;
-
public static class TestResultExtensions
{
- public static void DumpFailingTypes(this TestResult result, ITestOutputHelper outputHelper)
- {
- if (result.IsSuccessful)
- return;
-
- outputHelper.WriteLine("Failing Types:");
-
- foreach (var type in result.FailingTypes)
- outputHelper.WriteLine(type.FullName);
- }
-
+// TODO: Fix up
+// public static void DumpFailingTypes(this TestResult result, ITestOutputHelper outputHelper)
+// {
+// if (result.IsSuccessful)
+// return;
+//
+// outputHelper.WriteLine("Failing Types:");
+//
+// foreach (var type in result.FailingTypes)
+// outputHelper.WriteLine(type.FullName);
+// }
+//
public static TestResultAssertions Should(this TestResult result) => new(result);
}
\ No newline at end of file
diff --git a/tests/Architecture.Tests/Common/TypeExtensions.cs b/tests/Architecture.Tests/Common/TypeExtensions.cs
index 9406f542..5aa1da6e 100644
--- a/tests/Architecture.Tests/Common/TypeExtensions.cs
+++ b/tests/Architecture.Tests/Common/TypeExtensions.cs
@@ -1,15 +1,16 @@
-using Xunit.Abstractions;
+// using Xunit.Abstractions;
namespace SSW.CleanArchitecture.Architecture.UnitTests.Common;
-public static class TypeExtensions
-{
- public static void Dump(this IEnumerable types, ITestOutputHelper outputHelper)
- {
- if (!types.Any())
- outputHelper.WriteLine("No types found.");
-
- foreach (var type in types)
- outputHelper.WriteLine(type.FullName);
- }
-}
\ No newline at end of file
+// TODO: Fix up
+// public static class TypeExtensions
+// {
+// public static void Dump(this IEnumerable types, ITestOutputHelper outputHelper)
+// {
+// if (!types.Any())
+// outputHelper.WriteLine("No types found.");
+//
+// foreach (var type in types)
+// outputHelper.WriteLine(type.FullName);
+// }
+// }
\ No newline at end of file
diff --git a/tests/Architecture.Tests/DomainTests.cs b/tests/Architecture.Tests/DomainTests.cs
index 0423ab0a..c080aeee 100644
--- a/tests/Architecture.Tests/DomainTests.cs
+++ b/tests/Architecture.Tests/DomainTests.cs
@@ -12,7 +12,7 @@ public class DomainModel : TestBase
private static readonly Type DomainEvent = typeof(IDomainEvent);
private static readonly Type ValueObject = typeof(IValueObject);
- [Fact]
+ [Test]
public void DomainModel_ShouldInheritsBaseClasses()
{
// Arrange
@@ -37,7 +37,7 @@ public void DomainModel_ShouldInheritsBaseClasses()
result.Should().BeSuccessful();
}
- [Fact]
+ [Test]
public void EntitiesAndAggregates_ShouldHavePrivateParameterlessConstructor()
{
var entityTypes = Types.InAssembly(DomainAssembly)
diff --git a/tests/Architecture.Tests/Layer.cs b/tests/Architecture.Tests/Layer.cs
index d68d9ac0..94e553ab 100644
--- a/tests/Architecture.Tests/Layer.cs
+++ b/tests/Architecture.Tests/Layer.cs
@@ -4,7 +4,7 @@ namespace SSW.CleanArchitecture.Architecture.UnitTests;
public class Layer : TestBase
{
- [Fact]
+ [Test]
public void DomainLayer_Should_NotHaveDependencyOnApplication()
{
var result = Types.InAssembly(DomainAssembly)
@@ -15,7 +15,7 @@ public void DomainLayer_Should_NotHaveDependencyOnApplication()
result.Should().BeSuccessful();
}
- [Fact]
+ [Test]
public void DomainLayer_ShouldNotHaveDependencyOn_InfrastructureLayer()
{
var result = Types.InAssembly(DomainAssembly)
@@ -26,7 +26,7 @@ public void DomainLayer_ShouldNotHaveDependencyOn_InfrastructureLayer()
result.Should().BeSuccessful();
}
- [Fact]
+ [Test]
public void ApplicationLayer_ShouldNotHaveDependencyOn_InfrastructureLayer()
{
var result = Types.InAssembly(ApplicationAssembly)
@@ -37,7 +37,7 @@ public void ApplicationLayer_ShouldNotHaveDependencyOn_InfrastructureLayer()
result.Should().BeSuccessful();
}
- [Fact]
+ [Test]
public void InfrastructureLayer_ShouldNotHaveDependencyOn_PresentationLayer()
{
var result = Types.InAssembly(InfrastructureAssembly)
diff --git a/tests/Architecture.Tests/Presentation.cs b/tests/Architecture.Tests/Presentation.cs
index b5a746e5..a5ccce65 100644
--- a/tests/Architecture.Tests/Presentation.cs
+++ b/tests/Architecture.Tests/Presentation.cs
@@ -9,7 +9,7 @@ public class Presentation : TestBase
private static readonly Type IDbContext = typeof(IApplicationDbContext);
private static readonly Type DbContext = typeof(ApplicationDbContext);
- [Fact]
+ [Test]
public void Endpoints_ShouldNotReferenceDbContext()
{
var types = Types
diff --git a/tests/Architecture.Tests/Usings.cs b/tests/Architecture.Tests/Usings.cs
index 8c927eb7..3c84e45e 100644
--- a/tests/Architecture.Tests/Usings.cs
+++ b/tests/Architecture.Tests/Usings.cs
@@ -1 +1 @@
-global using Xunit;
\ No newline at end of file
+global using TUnit;
\ No newline at end of file
From 973cc6e9ec5e0115dd17e9ba9c9ed07f1248b2a3 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Mon, 18 Nov 2024 07:01:16 +1000
Subject: [PATCH 03/10] Fixed up test logging
---
.../Common/TestResultExtensions.cs | 25 ++++++++++---------
.../Common/TypeExtensions.cs | 25 ++++++++++---------
tests/Domain.UnitTests/Heroes/HeroTests.cs | 10 ++++++++
3 files changed, 36 insertions(+), 24 deletions(-)
diff --git a/tests/Architecture.Tests/Common/TestResultExtensions.cs b/tests/Architecture.Tests/Common/TestResultExtensions.cs
index 3432826c..2cfd8c9c 100644
--- a/tests/Architecture.Tests/Common/TestResultExtensions.cs
+++ b/tests/Architecture.Tests/Common/TestResultExtensions.cs
@@ -1,20 +1,21 @@
// using Xunit.Abstractions;
+
+using TUnit.Core.Logging;
using TestResult = NetArchTest.Rules.TestResult;
namespace SSW.CleanArchitecture.Architecture.UnitTests.Common;
public static class TestResultExtensions
{
-// TODO: Fix up
-// public static void DumpFailingTypes(this TestResult result, ITestOutputHelper outputHelper)
-// {
-// if (result.IsSuccessful)
-// return;
-//
-// outputHelper.WriteLine("Failing Types:");
-//
-// foreach (var type in result.FailingTypes)
-// outputHelper.WriteLine(type.FullName);
-// }
-//
+ public static void DumpFailingTypes(this TestResult result, ILogger logger)
+ {
+ if (result.IsSuccessful)
+ return;
+
+ logger.LogInformation("Failing Types:");
+
+ foreach (var type in result.FailingTypes)
+ logger.LogInformation(type.FullName);
+ }
+
public static TestResultAssertions Should(this TestResult result) => new(result);
}
\ No newline at end of file
diff --git a/tests/Architecture.Tests/Common/TypeExtensions.cs b/tests/Architecture.Tests/Common/TypeExtensions.cs
index 5aa1da6e..d6c111e2 100644
--- a/tests/Architecture.Tests/Common/TypeExtensions.cs
+++ b/tests/Architecture.Tests/Common/TypeExtensions.cs
@@ -1,16 +1,17 @@
// using Xunit.Abstractions;
+using TUnit.Core.Logging;
+
namespace SSW.CleanArchitecture.Architecture.UnitTests.Common;
-// TODO: Fix up
-// public static class TypeExtensions
-// {
-// public static void Dump(this IEnumerable types, ITestOutputHelper outputHelper)
-// {
-// if (!types.Any())
-// outputHelper.WriteLine("No types found.");
-//
-// foreach (var type in types)
-// outputHelper.WriteLine(type.FullName);
-// }
-// }
\ No newline at end of file
+public static class TypeExtensions
+{
+ public static void Dump(this IEnumerable types, ILogger outputHelper)
+ {
+ if (!types.Any())
+ outputHelper.LogInformation("No types found.");
+
+ foreach (var type in types)
+ outputHelper.LogInformation(type.FullName);
+ }
+}
\ No newline at end of file
diff --git a/tests/Domain.UnitTests/Heroes/HeroTests.cs b/tests/Domain.UnitTests/Heroes/HeroTests.cs
index 29634f88..5d226f78 100644
--- a/tests/Domain.UnitTests/Heroes/HeroTests.cs
+++ b/tests/Domain.UnitTests/Heroes/HeroTests.cs
@@ -1,9 +1,17 @@
using SSW.CleanArchitecture.Domain.Heroes;
+using TUnit.Core.Logging;
namespace SSW.CleanArchitecture.Domain.UnitTests.Heroes;
public class HeroTests
{
+ private readonly DefaultLogger _logger = new();
+
+ // public HeroTests()
+ // {
+ // _logger = logger;
+ // }
+
[Test]
[Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
[Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
@@ -27,6 +35,8 @@ public void HeroId_ShouldBeComparable(string stringGuid1, string stringGuid2, bo
[Test]
public void Create_WithValidNameAndAlias_ShouldSucceed()
{
+ _logger.LogError("Create_WithValidNameAndAlias_ShouldSucceed");
+
// Arrange
var name = "name";
var alias = "alias";
From e3f3637ef8b807194f334d41e722c69891d17118 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Mon, 18 Nov 2024 07:59:26 +1000
Subject: [PATCH 04/10] Got integration tests working
---
.../Common/Fixtures/IntegrationTestBase.cs | 24 +++---
.../IntegrationTestWebApplicationFactory.cs | 44 ----------
.../Common/Fixtures/TestingDatabaseFixture.cs | 57 -------------
.../Common/Fixtures/WebApplicationFactory.cs | 80 +++++++++++++++++++
.../Heroes/Commands/CreateHeroCommandTests.cs | 7 +-
.../Heroes/Commands/UpdateHeroCommandTests.cs | 9 ++-
.../Heroes/Queries/GetAllHeroesQueryTests.cs | 2 +-
.../Commands/AddHeroToTeamCommandTests.cs | 2 +-
.../Commands/CompleteMissionCommandTests.cs | 2 +-
.../Teams/Commands/CreateTeamCommandTests.cs | 2 +-
.../Commands/ExecuteMissionCommandTests.cs | 2 +-
.../Events/UpdatePowerLevelEventTests.cs | 2 +-
.../Teams/Queries/GetAllTeamsQueryTests.cs | 2 +-
tests/WebApi.IntegrationTests/Usings.cs | 4 +-
.../WebApi.IntegrationTests.csproj | 29 +++++--
15 files changed, 132 insertions(+), 136 deletions(-)
delete mode 100644 tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestWebApplicationFactory.cs
delete mode 100644 tests/WebApi.IntegrationTests/Common/Fixtures/TestingDatabaseFixture.cs
create mode 100644 tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
index 306915cd..1f2a2801 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
@@ -3,41 +3,41 @@
using Microsoft.Extensions.DependencyInjection;
using SSW.CleanArchitecture.Application.Common.Interfaces;
using SSW.CleanArchitecture.Infrastructure.Persistence;
+using TUnit.Core.Interfaces;
namespace WebApi.IntegrationTests.Common.Fixtures;
///
/// Integration tests inherit from this to access helper classes
///
-[Collection(TestingDatabaseFixtureCollection.Name)]
-public abstract class IntegrationTestBase : IAsyncLifetime
+// [Collection(TestingDatabaseFixtureCollection.Name)]
+public abstract class IntegrationTestBase : IAsyncInitializer
{
- private readonly IServiceScope _scope;
+ private IServiceScope _scope;
- private readonly TestingDatabaseFixture _fixture;
- protected IMediator Mediator { get; }
+ private WebApplicationFactory _fixture;
+ protected IMediator Mediator { get; private set; }
// TODO: Consider removing this as query results can be cached and cause bad test results
// Also, consider encapsulating this and only exposing a `Query` method that internally uses `AsNoTracking()`
// see: https://github.com/SSWConsulting/SSW.CleanArchitecture/issues/324
public IApplicationDbContext Context => _dbContext;
- private readonly ApplicationDbContext _dbContext;
+ private ApplicationDbContext _dbContext;
protected IQueryable GetQueryable() where T : class => _dbContext.Set().AsNoTracking();
- protected IntegrationTestBase(TestingDatabaseFixture fixture, ITestOutputHelper output)
+ protected IntegrationTestBase(WebApplicationFactory fixture)
{
_fixture = fixture;
- _fixture.Factory.Output = output;
+ }
+ public async Task InitializeAsync()
+ {
_scope = _fixture.ScopeFactory.CreateScope();
Mediator = _scope.ServiceProvider.GetRequiredService();
_dbContext = _scope.ServiceProvider.GetRequiredService();
- }
- public async Task InitializeAsync()
- {
await _fixture.ResetState();
}
@@ -46,7 +46,7 @@ protected async Task SaveChangesAsync(CancellationToken cancellationToken = defa
await Context.SaveChangesAsync(cancellationToken);
}
- protected HttpClient GetAnonymousClient() => _fixture.Factory.AnonymousClient.Value;
+ protected HttpClient GetAnonymousClient() => _fixture.AnonymousClient.Value;
public Task DisposeAsync()
{
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestWebApplicationFactory.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestWebApplicationFactory.cs
deleted file mode 100644
index bf1a6cae..00000000
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestWebApplicationFactory.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Meziantou.Extensions.Logging.Xunit;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using MigrationService.Initializers;
-using SSW.CleanArchitecture.WebApi;
-
-namespace WebApi.IntegrationTests.Common.Fixtures;
-
-///
-/// Host builder (services, DI and configuration) for integration tests
-///
-public class WebApiTestFactory : WebApplicationFactory
-{
- public DatabaseContainer Database { get; } = new();
-
- public ITestOutputHelper Output { get; set; } = null!;
-
- // NOTE: If you need an authenticated client, create a similar method that performance the authentication,
- // adds the appropriate headers, and returns the authenticated client
- // For an example of this see https://github.com/SSWConsulting/Northwind365
- public Lazy AnonymousClient => new(CreateClient());
-
- protected override void ConfigureWebHost(IWebHostBuilder builder)
- {
- // Redirect application logging to test output
- builder.ConfigureLogging(x =>
- {
- x.ClearProviders();
- x.AddFilter(level => level >= LogLevel.Information);
- x.Services.AddSingleton(new XUnitLoggerProvider(Output));
- });
-
- // Override default DB registration to use out Test Container instead
- builder.ConfigureTestServices(services =>
- {
- services.AddScoped();
- });
-
- builder.UseSetting("ConnectionStrings:clean-architecture", Database.ConnectionString);
- }
-}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/TestingDatabaseFixture.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/TestingDatabaseFixture.cs
deleted file mode 100644
index 123a62ac..00000000
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/TestingDatabaseFixture.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using MigrationService.Initializers;
-using Respawn;
-
-namespace WebApi.IntegrationTests.Common.Fixtures;
-
-///
-/// Initializes and resets the database before and after each test
-///
-// ReSharper disable once ClassNeverInstantiated.Global
-public class TestingDatabaseFixture : IAsyncLifetime
-{
- private string ConnectionString => Factory.Database.ConnectionString!;
-
- private Respawner _checkpoint = default!;
-
- public IServiceScopeFactory ScopeFactory { get; private set; } = default!;
-
- public readonly WebApiTestFactory Factory = new();
-
- public async Task InitializeAsync()
- {
- // Initialize DB Container
- await Factory.Database.InitializeAsync();
- ScopeFactory = Factory.Services.GetRequiredService();
-
- // Create and seed database
- using var scope = ScopeFactory.CreateScope();
- var warehouseInitializer = scope.ServiceProvider.GetRequiredService();
- await warehouseInitializer.EnsureDatabaseAsync(default);
- await warehouseInitializer.CreateSchemaAsync(true, default);
- // await warehouseInitializer.SeedDataAsync(default);
-
- // NOTE: If there are any tables you want to skip being reset, they can be configured here
- _checkpoint = await Respawner.CreateAsync(ConnectionString);
- }
-
- public async Task DisposeAsync()
- {
- await Factory.Database.DisposeAsync();
- }
-
- public async Task ResetState()
- {
- await _checkpoint.ResetAsync(ConnectionString);
- }
-}
-
-[CollectionDefinition(Name)]
-public class TestingDatabaseFixtureCollection : ICollectionFixture
-{
- // This class has no code, and is never created. Its purpose is simply
- // to be the place to apply [CollectionDefinition] and all the
- // ICollectionFixture<> interfaces.
-
- public const string Name = nameof(TestingDatabaseFixtureCollection);
-}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs
new file mode 100644
index 00000000..0100f480
--- /dev/null
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs
@@ -0,0 +1,80 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using MigrationService.Initializers;
+using Respawn;
+using SSW.CleanArchitecture.WebApi;
+using TUnit.Core.Interfaces;
+
+namespace WebApi.IntegrationTests.Common.Fixtures;
+
+///
+/// Initializes and resets the database before and after each test
+///
+// ReSharper disable once ClassNeverInstantiated.Global
+public class WebApplicationFactory: WebApplicationFactory, IAsyncInitializer
+{
+ private string ConnectionString => Database.ConnectionString!;
+
+ private Respawner _checkpoint = default!;
+
+ public IServiceScopeFactory ScopeFactory { get; private set; } = default!;
+
+ public async Task InitializeAsync()
+ {
+ // Initialize DB Container
+ await Database.InitializeAsync();
+ ScopeFactory = Services.GetRequiredService();
+
+ // Create and seed database
+ using var scope = ScopeFactory.CreateScope();
+ var warehouseInitializer = scope.ServiceProvider.GetRequiredService();
+ await warehouseInitializer.EnsureDatabaseAsync(default);
+ await warehouseInitializer.CreateSchemaAsync(true, default);
+
+ // NOTE: If there are any tables you want to skip being reset, they can be configured here
+ _checkpoint = await Respawner.CreateAsync(ConnectionString);
+ }
+
+ public override async ValueTask DisposeAsync()
+ {
+ await Database.DisposeAsync();
+ await base.DisposeAsync();
+ }
+
+ public async Task ResetState()
+ {
+ await _checkpoint.ResetAsync(ConnectionString);
+ }
+
+ public DatabaseContainer Database { get; } = new();
+
+ // public ITestOutputHelper Output { get; set; } = null!;
+
+ // NOTE: If you need an authenticated client, create a similar method that performance the authentication,
+ // adds the appropriate headers, and returns the authenticated client
+ // For an example of this see https://github.com/SSWConsulting/Northwind365
+ public Lazy AnonymousClient => new(CreateClient());
+
+ protected override void ConfigureWebHost(IWebHostBuilder builder)
+ {
+ // Redirect application logging to test output
+ builder.ConfigureLogging(x =>
+ {
+ x.ClearProviders();
+ x.AddFilter(level => level >= LogLevel.Information);
+ // TODO: Fix up logging
+ // x.Services.AddSingleton(new XUnitLoggerProvider(Output));
+ });
+
+ // Override default DB registration to use out Test Container instead
+ builder.ConfigureTestServices(services =>
+ {
+ services.AddScoped();
+ });
+
+ builder.UseSetting("ConnectionStrings:clean-architecture", Database.ConnectionString);
+ }
+}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs
index ae65965e..e83debb9 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs
@@ -6,10 +6,11 @@
namespace WebApi.IntegrationTests.Endpoints.Heroes.Commands;
-public class CreateHeroCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+[ClassDataSource(Shared = SharedType.PerTestSession)]
+public class CreateHeroCommandTests(WebApplicationFactory fixture)
+ : IntegrationTestBase(fixture)
{
- [Fact]
+ [Test]
public async Task Command_ShouldCreateHero()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs
index 4276552c..36fc0b37 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs
@@ -8,10 +8,11 @@
namespace WebApi.IntegrationTests.Endpoints.Heroes.Commands;
-public class UpdateHeroCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+[ClassDataSource(Shared = SharedType.PerTestSession)]
+public class UpdateHeroCommandTests(WebApplicationFactory fixture)
+ : IntegrationTestBase(fixture)
{
- [Fact]
+ [Test]
public async Task Command_ShouldUpdateHero()
{
// Arrange
@@ -50,7 +51,7 @@ public async Task Command_ShouldUpdateHero()
item.UpdatedAt.Should().BeCloseTo(createdTimeStamp, TimeSpan.FromSeconds(10));
}
- [Fact]
+ [Test]
public async Task Command_WhenHeroDoesNotExist_ShouldReturnNotFound()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs
index 0e49c2ff..8a4e5c3e 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs
@@ -8,7 +8,7 @@ namespace WebApi.IntegrationTests.Endpoints.Heroes.Queries;
public class GetAllHeroesQueryTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
- [Fact]
+ [Test]
public async Task Query_ShouldReturnAllHeroes()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs
index 06b39bab..18ec57e2 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs
@@ -10,7 +10,7 @@ namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
public class AddHeroToTeamCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
- [Fact]
+ [Test]
public async Task Command_ShouldAddHeroToTeam()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs
index 33e13e62..bc81bf6e 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs
@@ -10,7 +10,7 @@ namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
public class CompleteMissionCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
- [Fact]
+ [Test]
public async Task Command_ShouldCompleteMission()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs
index 96b604f6..112173f2 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs
@@ -9,7 +9,7 @@ namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
public class CreateTeamCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
- [Fact]
+ [Test]
public async Task Command_ShouldCreateTeam()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs
index a84e3fd0..dc4577d2 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs
@@ -12,7 +12,7 @@ namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
public class ExecuteMissionCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
- [Fact]
+ [Test]
public async Task Command_ShouldExecuteMission()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
index 0909b8a0..74c99fb5 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
@@ -13,7 +13,7 @@ namespace WebApi.IntegrationTests.Endpoints.Teams.Events;
public class UpdatePowerLevelEventTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
- [Fact]
+ [Test]
public async Task Command_UpdatePowerOnTeam()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs
index 881449af..2f43a4ae 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs
@@ -8,7 +8,7 @@ namespace WebApi.IntegrationTests.Endpoints.Teams.Queries;
public class GetAllTeamsQueryTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
- [Fact]
+ [Test]
public async Task Query_ShouldReturnAllTeams()
{
// Arrange
diff --git a/tests/WebApi.IntegrationTests/Usings.cs b/tests/WebApi.IntegrationTests/Usings.cs
index 339afbbd..a1bcbf32 100644
--- a/tests/WebApi.IntegrationTests/Usings.cs
+++ b/tests/WebApi.IntegrationTests/Usings.cs
@@ -1,3 +1,3 @@
-global using Xunit;
-global using Xunit.Abstractions;
+// global using Xunit;
+// global using Xunit.Abstractions;
global using FluentAssertions;
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj b/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
index c31cdb07..61e967d7 100644
--- a/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
+++ b/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
@@ -14,19 +14,19 @@
-
+
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
+
+
+
+
+
+
@@ -34,4 +34,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 84c0cd9daa5f1e280c9bf9871f355dbfb64fe03e Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Tue, 19 Nov 2024 21:27:28 +1000
Subject: [PATCH 05/10] Massive refactor of integration tests
---
.../Fixtures/CustomWebApplicationFactory.cs | 34 ++++++++
.../Common/Fixtures/DatabaseContainer.cs | 6 +-
.../Common/Fixtures/IntegrationTestBase.cs | 50 +++++-------
.../Common/Fixtures/SqlServerTestDatabase.cs | 42 ++++++++++
.../Common/Fixtures/Testing.cs | 44 ++++++++++
.../Common/Fixtures/WebApplicationFactory.cs | 80 -------------------
.../Heroes/Commands/CreateHeroCommandTests.cs | 7 +-
.../Heroes/Commands/UpdateHeroCommandTests.cs | 11 +--
.../Heroes/Queries/GetAllHeroesQueryTests.cs | 6 +-
.../Commands/AddHeroToTeamCommandTests.cs | 8 +-
.../Commands/CompleteMissionCommandTests.cs | 7 +-
.../Teams/Commands/CreateTeamCommandTests.cs | 6 +-
.../Commands/ExecuteMissionCommandTests.cs | 7 +-
.../Events/UpdatePowerLevelEventTests.cs | 7 +-
.../Teams/Queries/GetAllTeamsQueryTests.cs | 7 +-
.../WebApi.IntegrationTests.csproj | 21 -----
16 files changed, 173 insertions(+), 170 deletions(-)
create mode 100644 tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs
create mode 100644 tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
create mode 100644 tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
delete mode 100644 tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs
new file mode 100644
index 00000000..dfd477ad
--- /dev/null
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs
@@ -0,0 +1,34 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Extensions.Logging;
+using SSW.CleanArchitecture.WebApi;
+using System.Data.Common;
+
+namespace WebApi.IntegrationTests.Common.Fixtures;
+
+///
+/// Leverages default host setup to allow for integration testing
+///
+public class CustomWebApplicationFactory: WebApplicationFactory
+{
+ private readonly DbConnection _dbConnection;
+
+ public CustomWebApplicationFactory(DbConnection dbConnection)
+ {
+ _dbConnection = dbConnection;
+ }
+
+ protected override void ConfigureWebHost(IWebHostBuilder builder)
+ {
+ // Redirect application logging to test output
+ builder.ConfigureLogging(x =>
+ {
+ x.ClearProviders();
+ x.AddFilter(level => level >= LogLevel.Information);
+ // TODO: Fix up logging
+ // x.Services.AddSingleton(new XUnitLoggerProvider(Output));
+ });
+
+ builder.UseSetting("ConnectionStrings:clean-architecture", _dbConnection.ConnectionString);
+ }
+}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
index 2db2d3c4..7a402ec3 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
@@ -1,3 +1,5 @@
+using Microsoft.Data.SqlClient;
+using MigrationService.Initializers;
using Polly;
using Testcontainers.MsSql;
@@ -18,12 +20,12 @@ public class DatabaseContainer : IAsyncDisposable
private const int MaxRetries = 5;
- public string? ConnectionString { get; private set; }
+ public SqlConnection? ConnectionString { get; private set; }
public async Task InitializeAsync()
{
await StartWithRetry();
- ConnectionString = _container.GetConnectionString();
+ ConnectionString = new SqlConnection(_container.GetConnectionString());
}
private async Task StartWithRetry()
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
index 1f2a2801..2aedbd57 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
@@ -1,6 +1,7 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
+using OpenTelemetry.Instrumentation.Http;
using SSW.CleanArchitecture.Application.Common.Interfaces;
using SSW.CleanArchitecture.Infrastructure.Persistence;
using TUnit.Core.Interfaces;
@@ -10,47 +11,40 @@ namespace WebApi.IntegrationTests.Common.Fixtures;
///
/// Integration tests inherit from this to access helper classes
///
-// [Collection(TestingDatabaseFixtureCollection.Name)]
-public abstract class IntegrationTestBase : IAsyncInitializer
+[NotInParallel]
+public abstract class IntegrationTestBaseV2 : IDisposable
{
- private IServiceScope _scope;
+ private readonly ApplicationDbContext _dbContext;
+ private readonly IServiceScope _scope;
+ protected readonly ISender Mediator;
- private WebApplicationFactory _fixture;
- protected IMediator Mediator { get; private set; }
-
- // TODO: Consider removing this as query results can be cached and cause bad test results
- // Also, consider encapsulating this and only exposing a `Query` method that internally uses `AsNoTracking()`
- // see: https://github.com/SSWConsulting/SSW.CleanArchitecture/issues/324
- public IApplicationDbContext Context => _dbContext;
-
- private ApplicationDbContext _dbContext;
-
- protected IQueryable GetQueryable() where T : class => _dbContext.Set().AsNoTracking();
-
- protected IntegrationTestBase(WebApplicationFactory fixture)
+ public IntegrationTestBaseV2()
{
- _fixture = fixture;
+ _scope = Testing.CreateScope();
+ _dbContext = _scope.ServiceProvider.GetRequiredService();
+ Mediator = _scope.ServiceProvider.GetRequiredService();
}
- public async Task InitializeAsync()
- {
- _scope = _fixture.ScopeFactory.CreateScope();
- Mediator = _scope.ServiceProvider.GetRequiredService();
- _dbContext = _scope.ServiceProvider.GetRequiredService();
+ protected IQueryable GetQueryable() where T : class => _dbContext.Set().AsNoTracking();
- await _fixture.ResetState();
+ protected async Task AddAsync(TEntity entity)
+ where TEntity : class
+ {
+ await _dbContext.AddAsync(entity);
+ await _dbContext.SaveChangesAsync();
}
- protected async Task SaveChangesAsync(CancellationToken cancellationToken = default)
+ protected async Task AddRangeAsync(IEnumerable entities)
+ where TEntity : class
{
- await Context.SaveChangesAsync(cancellationToken);
+ await _dbContext.AddRangeAsync(entities);
+ await _dbContext.SaveChangesAsync();
}
- protected HttpClient GetAnonymousClient() => _fixture.AnonymousClient.Value;
+ protected HttpClient GetAnonymousClient() => Testing.AnonymousClient.Value;
- public Task DisposeAsync()
+ public void Dispose()
{
_scope.Dispose();
- return Task.CompletedTask;
}
}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
new file mode 100644
index 00000000..190d6fc1
--- /dev/null
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
@@ -0,0 +1,42 @@
+using Microsoft.EntityFrameworkCore;
+using Respawn;
+using SSW.CleanArchitecture.Infrastructure.Persistence;
+using System.Data.Common;
+
+namespace WebApi.IntegrationTests.Common.Fixtures;
+
+public class SqlServerTestDatabase : IAsyncDisposable
+{
+ private static DatabaseContainer _database = new();
+ private Respawner _checkpoint = null!;
+
+ ///
+ /// Create and seed database
+ ///
+ public async Task InitializeAsync()
+ {
+ await _database.InitializeAsync();
+
+ var options = new DbContextOptionsBuilder()
+ .UseSqlServer(_database.ConnectionString)
+ .Options;
+
+ await using var dbContext = new ApplicationDbContext(options);
+ await dbContext.Database.MigrateAsync();
+
+ _checkpoint = await Respawner.CreateAsync(_database.ConnectionString,
+ new RespawnerOptions { TablesToIgnore = ["__EFMigrationsHistory"] });
+ }
+
+ public DbConnection GetConnection() => _database.ConnectionString;
+
+ public async Task ResetAsync()
+ {
+ await _checkpoint.ResetAsync(_database.ConnectionString);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await _database.DisposeAsync();
+ }
+}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
new file mode 100644
index 00000000..8e8fb22b
--- /dev/null
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
@@ -0,0 +1,44 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace WebApi.IntegrationTests.Common.Fixtures;
+
+public static class Testing
+{
+ private static SqlServerTestDatabase _database = new();
+ private static CustomWebApplicationFactory _factory = null!;
+ private static IServiceScopeFactory _scopeFactory = null!;
+
+ [Before(Assembly)]
+ public static async Task GlobalSetup()
+ {
+ await _database.InitializeAsync();
+ _factory = new CustomWebApplicationFactory(_database.GetConnection());
+ _scopeFactory = _factory.Services.GetRequiredService();
+ }
+
+ [BeforeEvery(Test)]
+ public static async Task TestSetup(TestContext context)
+ {
+ await _database.ResetAsync();
+ }
+
+ [AfterEvery(Test)]
+ public static async Task TestCleanUp(TestContext context)
+ {
+ // TODO: Anything to do here?
+ }
+
+ [After(Assembly)]
+ public static async Task GlobalCleanUp()
+ {
+ await _database.DisposeAsync();
+ await _factory.DisposeAsync();
+ }
+
+ // NOTE: If you need an authenticated client, create a similar method that performance the authentication,
+ // adds the appropriate headers, and returns the authenticated client
+ // For an example of this see https://github.com/SSWConsulting/Northwind365
+ public static Lazy AnonymousClient => new(_factory.CreateClient());
+
+ public static IServiceScope CreateScope() => _scopeFactory.CreateScope();
+}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs
deleted file mode 100644
index 0100f480..00000000
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/WebApplicationFactory.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using MigrationService.Initializers;
-using Respawn;
-using SSW.CleanArchitecture.WebApi;
-using TUnit.Core.Interfaces;
-
-namespace WebApi.IntegrationTests.Common.Fixtures;
-
-///
-/// Initializes and resets the database before and after each test
-///
-// ReSharper disable once ClassNeverInstantiated.Global
-public class WebApplicationFactory: WebApplicationFactory, IAsyncInitializer
-{
- private string ConnectionString => Database.ConnectionString!;
-
- private Respawner _checkpoint = default!;
-
- public IServiceScopeFactory ScopeFactory { get; private set; } = default!;
-
- public async Task InitializeAsync()
- {
- // Initialize DB Container
- await Database.InitializeAsync();
- ScopeFactory = Services.GetRequiredService();
-
- // Create and seed database
- using var scope = ScopeFactory.CreateScope();
- var warehouseInitializer = scope.ServiceProvider.GetRequiredService();
- await warehouseInitializer.EnsureDatabaseAsync(default);
- await warehouseInitializer.CreateSchemaAsync(true, default);
-
- // NOTE: If there are any tables you want to skip being reset, they can be configured here
- _checkpoint = await Respawner.CreateAsync(ConnectionString);
- }
-
- public override async ValueTask DisposeAsync()
- {
- await Database.DisposeAsync();
- await base.DisposeAsync();
- }
-
- public async Task ResetState()
- {
- await _checkpoint.ResetAsync(ConnectionString);
- }
-
- public DatabaseContainer Database { get; } = new();
-
- // public ITestOutputHelper Output { get; set; } = null!;
-
- // NOTE: If you need an authenticated client, create a similar method that performance the authentication,
- // adds the appropriate headers, and returns the authenticated client
- // For an example of this see https://github.com/SSWConsulting/Northwind365
- public Lazy AnonymousClient => new(CreateClient());
-
- protected override void ConfigureWebHost(IWebHostBuilder builder)
- {
- // Redirect application logging to test output
- builder.ConfigureLogging(x =>
- {
- x.ClearProviders();
- x.AddFilter(level => level >= LogLevel.Information);
- // TODO: Fix up logging
- // x.Services.AddSingleton(new XUnitLoggerProvider(Output));
- });
-
- // Override default DB registration to use out Test Container instead
- builder.ConfigureTestServices(services =>
- {
- services.AddScoped();
- });
-
- builder.UseSetting("ConnectionStrings:clean-architecture", Database.ConnectionString);
- }
-}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs
index e83debb9..9d41e0b2 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs
@@ -1,14 +1,13 @@
using Microsoft.EntityFrameworkCore;
using SSW.CleanArchitecture.Application.UseCases.Heroes.Commands.CreateHero;
+using SSW.CleanArchitecture.Domain.Heroes;
using System.Net;
using System.Net.Http.Json;
using WebApi.IntegrationTests.Common.Fixtures;
namespace WebApi.IntegrationTests.Endpoints.Heroes.Commands;
-[ClassDataSource(Shared = SharedType.PerTestSession)]
-public class CreateHeroCommandTests(WebApplicationFactory fixture)
- : IntegrationTestBase(fixture)
+public class CreateHeroCommandTests : IntegrationTestBaseV2
{
[Test]
public async Task Command_ShouldCreateHero()
@@ -31,7 +30,7 @@ public async Task Command_ShouldCreateHero()
// Assert
result.StatusCode.Should().Be(HttpStatusCode.Created);
- var item = await Context.Heroes.AsNoTracking().FirstAsync();
+ var item = await GetQueryable().FirstAsync();
item.Should().NotBeNull();
item.Name.Should().Be(cmd.Name);
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs
index 36fc0b37..7ca89042 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs
@@ -8,9 +8,7 @@
namespace WebApi.IntegrationTests.Endpoints.Heroes.Commands;
-[ClassDataSource(Shared = SharedType.PerTestSession)]
-public class UpdateHeroCommandTests(WebApplicationFactory fixture)
- : IntegrationTestBase(fixture)
+public class UpdateHeroCommandTests : IntegrationTestBaseV2
{
[Test]
public async Task Command_ShouldUpdateHero()
@@ -19,8 +17,7 @@ public async Task Command_ShouldUpdateHero()
var heroName = "2021-01-01T00:00:00Z";
var heroAlias = "2021-01-01T00:00:00Z-alias";
var hero = HeroFactory.Generate();
- Context.Heroes.Add(hero);
- await Context.SaveChangesAsync();
+ await AddAsync(hero);
(string Name, int PowerLevel)[] powers =
[
("Heat vision", 7),
@@ -40,7 +37,7 @@ public async Task Command_ShouldUpdateHero()
// Assert
result.StatusCode.Should().Be(HttpStatusCode.NoContent);
- Hero item = await Context.Heroes.AsNoTracking().FirstAsync(dbHero => dbHero.Id == hero.Id);
+ var item = await GetQueryable().FirstAsync(dbHero => dbHero.Id == hero.Id);
item.Should().NotBeNull();
item.Name.Should().Be(cmd.Name);
@@ -68,7 +65,7 @@ public async Task Command_WhenHeroDoesNotExist_ShouldReturnNotFound()
// Assert
result.StatusCode.Should().Be(HttpStatusCode.NotFound);
- Hero? item = await Context.Heroes.AsNoTracking().FirstOrDefaultAsync(dbHero => dbHero.Id == heroId);
+ var item = await GetQueryable().FirstOrDefaultAsync(dbHero => dbHero.Id == heroId);
item.Should().BeNull();
}
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs
index 8a4e5c3e..6e046435 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Queries/GetAllHeroesQueryTests.cs
@@ -5,8 +5,7 @@
namespace WebApi.IntegrationTests.Endpoints.Heroes.Queries;
-public class GetAllHeroesQueryTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+public class GetAllHeroesQueryTests : IntegrationTestBaseV2
{
[Test]
public async Task Query_ShouldReturnAllHeroes()
@@ -14,8 +13,7 @@ public async Task Query_ShouldReturnAllHeroes()
// Arrange
const int entityCount = 10;
var entities = HeroFactory.Generate(entityCount);
- await Context.Heroes.AddRangeAsync(entities);
- await Context.SaveChangesAsync();
+ await AddRangeAsync(entities);
var client = GetAnonymousClient();
// Act
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs
index 18ec57e2..93cee3c6 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs
@@ -7,8 +7,7 @@
namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
-public class AddHeroToTeamCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+public class AddHeroToTeamCommandTests : IntegrationTestBaseV2
{
[Test]
public async Task Command_ShouldAddHeroToTeam()
@@ -16,9 +15,8 @@ public async Task Command_ShouldAddHeroToTeam()
// Arrange
var hero = HeroFactory.Generate();
var team = TeamFactory.Generate();
- Context.Heroes.Add(hero);
- Context.Teams.Add(team);
- await Context.SaveChangesAsync();
+ await AddAsync(hero);
+ await AddAsync(team);
var teamId = team.Id.Value;
var heroId = hero.Id.Value;
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs
index bc81bf6e..661b39dd 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CompleteMissionCommandTests.cs
@@ -7,8 +7,7 @@
namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
-public class CompleteMissionCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+public class CompleteMissionCommandTests : IntegrationTestBaseV2
{
[Test]
public async Task Command_ShouldCompleteMission()
@@ -18,8 +17,8 @@ public async Task Command_ShouldCompleteMission()
var team = TeamFactory.Generate();
team.AddHero(hero);
team.ExecuteMission("Save the world");
- Context.Teams.Add(team);
- await Context.SaveChangesAsync();
+ await AddAsync(team);
+ // await Context.SaveChangesAsync();
var teamId = team.Id.Value;
var client = GetAnonymousClient();
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs
index 112173f2..53c2a785 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/CreateTeamCommandTests.cs
@@ -1,13 +1,13 @@
using Microsoft.EntityFrameworkCore;
using SSW.CleanArchitecture.Application.UseCases.Teams.Commands.CreateTeam;
+using SSW.CleanArchitecture.Domain.Teams;
using System.Net;
using System.Net.Http.Json;
using WebApi.IntegrationTests.Common.Fixtures;
namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
-public class CreateTeamCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+public class CreateTeamCommandTests : IntegrationTestBaseV2
{
[Test]
public async Task Command_ShouldCreateTeam()
@@ -21,7 +21,7 @@ public async Task Command_ShouldCreateTeam()
// Assert
result.StatusCode.Should().Be(HttpStatusCode.Created);
- var item = await Context.Teams.AsNoTracking().FirstAsync();
+ var item = await GetQueryable().FirstAsync();
item.Should().NotBeNull();
item.Name.Should().Be(cmd.Name);
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs
index dc4577d2..6bb64be0 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/ExecuteMissionCommandTests.cs
@@ -9,8 +9,7 @@
namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;
-public class ExecuteMissionCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+public class ExecuteMissionCommandTests : IntegrationTestBaseV2
{
[Test]
public async Task Command_ShouldExecuteMission()
@@ -19,8 +18,8 @@ public async Task Command_ShouldExecuteMission()
var hero = HeroFactory.Generate();
var team = TeamFactory.Generate();
team.AddHero(hero);
- Context.Teams.Add(team);
- await Context.SaveChangesAsync();
+ await AddAsync(team);
+ // await Context.SaveChangesAsync();
var teamId = team.Id.Value;
var client = GetAnonymousClient();
var request = new ExecuteMissionCommand("Save the world");
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
index 74c99fb5..231e66ea 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
@@ -10,8 +10,7 @@
namespace WebApi.IntegrationTests.Endpoints.Teams.Events;
-public class UpdatePowerLevelEventTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+public class UpdatePowerLevelEventTests : IntegrationTestBaseV2
{
[Test]
public async Task Command_UpdatePowerOnTeam()
@@ -22,8 +21,8 @@ public async Task Command_UpdatePowerOnTeam()
List powers = [new Power("Strength", 10)];
hero.UpdatePowers(powers);
team.AddHero(hero);
- Context.Teams.Add(team);
- await Context.SaveChangesAsync();
+ await AddAsync(team);
+ // await Context.SaveChangesAsync();
powers.Add(new Power("Speed", 5));
var powerDtos = powers.Select(p => new UpdateHeroPowerDto { Name = p.Name, PowerLevel = p.PowerLevel });
var cmd = new UpdateHeroCommand(hero.Name, hero.Alias, powerDtos);
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs
index 2f43a4ae..ca6d642b 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Queries/GetAllTeamsQueryTests.cs
@@ -5,8 +5,7 @@
namespace WebApi.IntegrationTests.Endpoints.Teams.Queries;
-public class GetAllTeamsQueryTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
- : IntegrationTestBase(fixture, output)
+public class GetAllTeamsQueryTests : IntegrationTestBaseV2
{
[Test]
public async Task Query_ShouldReturnAllTeams()
@@ -14,8 +13,8 @@ public async Task Query_ShouldReturnAllTeams()
// Arrange
const int entityCount = 10;
var entities = TeamFactory.Generate(entityCount);
- await Context.Teams.AddRangeAsync(entities);
- await Context.SaveChangesAsync();
+ await AddRangeAsync(entities);
+ // await Context.SaveChangesAsync();
var client = GetAnonymousClient();
// Act
diff --git a/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj b/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
index 61e967d7..7138230b 100644
--- a/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
+++ b/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
@@ -14,7 +14,6 @@
-
@@ -22,11 +21,6 @@
-
-
-
-
-
@@ -34,19 +28,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From ed27031ba7671da2058f6067dd7ad7e0f404eb64 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Wed, 20 Nov 2024 07:35:05 +1000
Subject: [PATCH 06/10] Got integration tests working
---
.editorconfig | 2 +-
.../Common/Fixtures/DatabaseContainer.cs | 6 +++--
.../Common/Fixtures/IntegrationTestBase.cs | 13 ++++------
.../Common/Fixtures/SqlServerTestDatabase.cs | 24 +++++++++++++++----
.../Common/Fixtures/Testing.cs | 4 ++--
.../WebApi.IntegrationTests.csproj | 4 ++++
6 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/.editorconfig b/.editorconfig
index e3ba3496..f65322c8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -242,7 +242,7 @@ dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
-dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = _camelcase
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
index 7a402ec3..331f5542 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
@@ -13,6 +13,8 @@ public class DatabaseContainer : IAsyncDisposable
private readonly MsSqlContainer _container = new MsSqlBuilder()
.WithImage("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04")
.WithName($"CleanArchitecture-IntegrationTests-{Guid.CreateVersion7()}")
+ // .WithCommand("sqlcmd -Q \"CREATE DATABASE [CleanArchitecture-IntegrationTests]\"")
+ // .WithCommand("/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P Password123 -Q \"CREATE DATABASE [MyDatabase]\"")
.WithPassword("Password123")
.WithPortBinding(1433, true)
.WithAutoRemove(true)
@@ -20,12 +22,12 @@ public class DatabaseContainer : IAsyncDisposable
private const int MaxRetries = 5;
- public SqlConnection? ConnectionString { get; private set; }
+ public SqlConnection? Connection { get; private set; }
public async Task InitializeAsync()
{
await StartWithRetry();
- ConnectionString = new SqlConnection(_container.GetConnectionString());
+ Connection = new SqlConnection(_container.GetConnectionString());
}
private async Task StartWithRetry()
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
index 2aedbd57..9bd93b4e 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
@@ -1,10 +1,6 @@
-using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
-using OpenTelemetry.Instrumentation.Http;
-using SSW.CleanArchitecture.Application.Common.Interfaces;
using SSW.CleanArchitecture.Infrastructure.Persistence;
-using TUnit.Core.Interfaces;
namespace WebApi.IntegrationTests.Common.Fixtures;
@@ -14,15 +10,14 @@ namespace WebApi.IntegrationTests.Common.Fixtures;
[NotInParallel]
public abstract class IntegrationTestBaseV2 : IDisposable
{
- private readonly ApplicationDbContext _dbContext;
- private readonly IServiceScope _scope;
- protected readonly ISender Mediator;
+ private ApplicationDbContext _dbContext = null!;
+ private IServiceScope _scope = null!;
- public IntegrationTestBaseV2()
+ [Before(Test)]
+ public void TestSetup()
{
_scope = Testing.CreateScope();
_dbContext = _scope.ServiceProvider.GetRequiredService();
- Mediator = _scope.ServiceProvider.GetRequiredService();
}
protected IQueryable GetQueryable() where T : class => _dbContext.Set().AsNoTracking();
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
index 190d6fc1..29b96c61 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
@@ -1,3 +1,4 @@
+using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Respawn;
using SSW.CleanArchitecture.Infrastructure.Persistence;
@@ -9,6 +10,7 @@ public class SqlServerTestDatabase : IAsyncDisposable
{
private static DatabaseContainer _database = new();
private Respawner _checkpoint = null!;
+ private string _connectionString = null!;
///
/// Create and seed database
@@ -17,22 +19,34 @@ public async Task InitializeAsync()
{
await _database.InitializeAsync();
+ var builder = new SqlConnectionStringBuilder(_database.Connection.ConnectionString)
+ {
+ InitialCatalog = "CleanArchitecture-IntegrationTests"
+ };
+
+ _connectionString = builder.ConnectionString;
+
+ // _connection = new SqlConnection(_database.Connection.ConnectionString);
+ // _connection.da("CleanArchitecture-IntegrationTests");
+
+ // _connection = _database.Connection;
+
var options = new DbContextOptionsBuilder()
- .UseSqlServer(_database.ConnectionString)
+ .UseSqlServer(_connectionString)
.Options;
- await using var dbContext = new ApplicationDbContext(options);
+ using var dbContext = new ApplicationDbContext(options);
await dbContext.Database.MigrateAsync();
- _checkpoint = await Respawner.CreateAsync(_database.ConnectionString,
+ _checkpoint = await Respawner.CreateAsync(_connectionString,
new RespawnerOptions { TablesToIgnore = ["__EFMigrationsHistory"] });
}
- public DbConnection GetConnection() => _database.ConnectionString;
+ public DbConnection GetConnection() => new SqlConnection(_connectionString);
public async Task ResetAsync()
{
- await _checkpoint.ResetAsync(_database.ConnectionString);
+ await _checkpoint.ResetAsync(_connectionString);
}
public async ValueTask DisposeAsync()
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
index 8e8fb22b..c06d918b 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
@@ -19,13 +19,13 @@ public static async Task GlobalSetup()
[BeforeEvery(Test)]
public static async Task TestSetup(TestContext context)
{
- await _database.ResetAsync();
+ // Nothing to do yet
}
[AfterEvery(Test)]
public static async Task TestCleanUp(TestContext context)
{
- // TODO: Anything to do here?
+ await _database.ResetAsync();
}
[After(Assembly)]
diff --git a/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj b/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
index 7138230b..ee3f1ece 100644
--- a/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
+++ b/tests/WebApi.IntegrationTests/WebApi.IntegrationTests.csproj
@@ -28,4 +28,8 @@
+
+
+
+
From c6d3c0d48dfb0e0e206ef04189f351815c483602 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Wed, 20 Nov 2024 07:53:40 +1000
Subject: [PATCH 07/10] Add handling for eventual consistency.
---
.../Common/Fixtures/DatabaseContainer.cs | 4 +---
.../Common/Fixtures/IntegrationTestBase.cs | 5 +++++
tests/WebApi.IntegrationTests/Common/Wait.cs | 14 ++++++++++++++
.../Teams/Events/UpdatePowerLevelEventTests.cs | 2 ++
4 files changed, 22 insertions(+), 3 deletions(-)
create mode 100644 tests/WebApi.IntegrationTests/Common/Wait.cs
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
index 331f5542..985c84b8 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
@@ -12,9 +12,7 @@ public class DatabaseContainer : IAsyncDisposable
{
private readonly MsSqlContainer _container = new MsSqlBuilder()
.WithImage("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04")
- .WithName($"CleanArchitecture-IntegrationTests-{Guid.CreateVersion7()}")
- // .WithCommand("sqlcmd -Q \"CREATE DATABASE [CleanArchitecture-IntegrationTests]\"")
- // .WithCommand("/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P Password123 -Q \"CREATE DATABASE [MyDatabase]\"")
+ .WithName($"CleanArchitecture-IntegrationTests-{Guid.NewGuid()}")
.WithPassword("Password123")
.WithPortBinding(1433, true)
.WithAutoRemove(true)
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
index 9bd93b4e..fb811779 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs
@@ -36,6 +36,11 @@ protected async Task AddRangeAsync(IEnumerable entities)
await _dbContext.SaveChangesAsync();
}
+ protected async Task SaveAsync()
+ {
+ await _dbContext.SaveChangesAsync();
+ }
+
protected HttpClient GetAnonymousClient() => Testing.AnonymousClient.Value;
public void Dispose()
diff --git a/tests/WebApi.IntegrationTests/Common/Wait.cs b/tests/WebApi.IntegrationTests/Common/Wait.cs
new file mode 100644
index 00000000..8e7e9657
--- /dev/null
+++ b/tests/WebApi.IntegrationTests/Common/Wait.cs
@@ -0,0 +1,14 @@
+namespace WebApi.IntegrationTests.Common;
+
+internal static class Wait
+{
+ private const int Milliseconds = 1000;
+
+ ///
+ /// Add a delay to allow the event to be processed
+ ///
+ internal static async Task ForEventualConsistency()
+ {
+ await Task.Delay(Milliseconds);
+ }
+}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
index 231e66ea..3201a024 100644
--- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
+++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs
@@ -5,6 +5,7 @@
using SSW.CleanArchitecture.Domain.Teams;
using System.Net;
using System.Net.Http.Json;
+using WebApi.IntegrationTests.Common;
using WebApi.IntegrationTests.Common.Factories;
using WebApi.IntegrationTests.Common.Fixtures;
@@ -33,6 +34,7 @@ public async Task Command_UpdatePowerOnTeam()
var result = await client.PutAsJsonAsync($"/api/heroes/{cmd.HeroId}", cmd);
// Assert
+ await Wait.ForEventualConsistency();
var updatedTeam = await GetQueryable()
.WithSpecification(new TeamByIdSpec(team.Id))
.FirstOrDefaultAsync();
From 58c5bd2d17e1ad908b9ccfb9518b7f60b9becd18 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Wed, 20 Nov 2024 07:55:00 +1000
Subject: [PATCH 08/10] Tidy up code
---
.../Common/Fixtures/DatabaseContainer.cs | 1 -
.../Common/Fixtures/SqlServerTestDatabase.cs | 7 +------
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
index 985c84b8..b8c7a329 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/DatabaseContainer.cs
@@ -1,5 +1,4 @@
using Microsoft.Data.SqlClient;
-using MigrationService.Initializers;
using Polly;
using Testcontainers.MsSql;
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
index 29b96c61..eceeddfd 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/SqlServerTestDatabase.cs
@@ -8,7 +8,7 @@ namespace WebApi.IntegrationTests.Common.Fixtures;
public class SqlServerTestDatabase : IAsyncDisposable
{
- private static DatabaseContainer _database = new();
+ private readonly DatabaseContainer _database = new();
private Respawner _checkpoint = null!;
private string _connectionString = null!;
@@ -26,11 +26,6 @@ public async Task InitializeAsync()
_connectionString = builder.ConnectionString;
- // _connection = new SqlConnection(_database.Connection.ConnectionString);
- // _connection.da("CleanArchitecture-IntegrationTests");
-
- // _connection = _database.Connection;
-
var options = new DbContextOptionsBuilder()
.UseSqlServer(_connectionString)
.Options;
From e354f406ca40343b91c34799e7914d24095d1099 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Sat, 23 Nov 2024 06:46:39 +1000
Subject: [PATCH 09/10] Remove unneeded constructor
---
tests/Domain.UnitTests/Heroes/HeroTests.cs | 5 -----
1 file changed, 5 deletions(-)
diff --git a/tests/Domain.UnitTests/Heroes/HeroTests.cs b/tests/Domain.UnitTests/Heroes/HeroTests.cs
index 5d226f78..192862d1 100644
--- a/tests/Domain.UnitTests/Heroes/HeroTests.cs
+++ b/tests/Domain.UnitTests/Heroes/HeroTests.cs
@@ -7,11 +7,6 @@ public class HeroTests
{
private readonly DefaultLogger _logger = new();
- // public HeroTests()
- // {
- // _logger = logger;
- // }
-
[Test]
[Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "cc3431a8-4a31-4f76-af64-e8198279d7a4", false)]
[Arguments("c8ad9974-ca93-44a5-9215-2f4d9e866c7a", "c8ad9974-ca93-44a5-9215-2f4d9e866c7a", true)]
From 52265cfea2adb7f24faae06de0841252b10384a6 Mon Sep 17 00:00:00 2001
From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com>
Date: Sat, 23 Nov 2024 10:07:34 +1000
Subject: [PATCH 10/10] Add TUnit logger
---
.../Commands/CreateHero/CreateHeroCommand.cs | 7 ++-
.../Fixtures/CustomWebApplicationFactory.cs | 46 +++++++++++++++++--
.../Common/Fixtures/Testing.cs | 2 +-
3 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs b/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs
index 0b8bb357..112b16a2 100644
--- a/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs
+++ b/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs
@@ -1,4 +1,5 @@
-using SSW.CleanArchitecture.Application.Common.Interfaces;
+using Microsoft.Extensions.Logging;
+using SSW.CleanArchitecture.Application.Common.Interfaces;
using SSW.CleanArchitecture.Domain.Heroes;
namespace SSW.CleanArchitecture.Application.UseCases.Heroes.Commands.CreateHero;
@@ -9,11 +10,13 @@ public sealed record CreateHeroCommand(
IEnumerable Powers) : IRequest>;
// ReSharper disable once UnusedType.Global
-public sealed class CreateHeroCommandHandler(IApplicationDbContext dbContext)
+public sealed class CreateHeroCommandHandler(IApplicationDbContext dbContext, ILogger logger)
: IRequestHandler>
{
public async Task> Handle(CreateHeroCommand request, CancellationToken cancellationToken)
{
+ logger.LogError("Creating hero with name {Name} and alias {Alias}", request.Name, request.Alias);
+
var hero = Hero.Create(request.Name, request.Alias);
var powers = request.Powers.Select(p => new Power(p.Name, p.PowerLevel));
hero.UpdatePowers(powers);
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs
index dfd477ad..dcd363e6 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/CustomWebApplicationFactory.cs
@@ -1,8 +1,12 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SSW.CleanArchitecture.WebApi;
using System.Data.Common;
+using TUnit.Core.Logging;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
+using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace WebApi.IntegrationTests.Common.Fixtures;
@@ -21,14 +25,46 @@ public CustomWebApplicationFactory(DbConnection dbConnection)
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// Redirect application logging to test output
- builder.ConfigureLogging(x =>
+ builder.ConfigureLogging(logging =>
{
- x.ClearProviders();
- x.AddFilter(level => level >= LogLevel.Information);
- // TODO: Fix up logging
- // x.Services.AddSingleton(new XUnitLoggerProvider(Output));
+ logging.ClearProviders();
+ // TODO: This doesn't seem to log everything. Need to investigate further
+ logging.AddProvider(new TUnitLoggerProvider());
});
builder.UseSetting("ConnectionStrings:clean-architecture", _dbConnection.ConnectionString);
}
+}
+
+public class TUnitLoggerProvider : ILoggerProvider
+{
+ public ILogger CreateLogger(string categoryName)
+ {
+ return new TUnitDefaultLogger();
+ }
+
+ public void Dispose()
+ {
+ // Dispose resources if any
+ }
+}
+
+public class TUnitDefaultLogger : ILogger
+{
+ private readonly TUnit.Core.Logging.DefaultLogger _logger = new();
+
+ public IDisposable BeginScope(TState state)
+ {
+ return null!;
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ return true;
+ }
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
+ {
+ _logger.LogInformation(formatter(state, exception));
+ }
}
\ No newline at end of file
diff --git a/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs b/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
index c06d918b..e7059c55 100644
--- a/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
+++ b/tests/WebApi.IntegrationTests/Common/Fixtures/Testing.cs
@@ -4,7 +4,7 @@ namespace WebApi.IntegrationTests.Common.Fixtures;
public static class Testing
{
- private static SqlServerTestDatabase _database = new();
+ private static readonly SqlServerTestDatabase _database = new();
private static CustomWebApplicationFactory _factory = null!;
private static IServiceScopeFactory _scopeFactory = null!;