-
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added architecture tests for horizontal layers and vertical slices. #10
- Loading branch information
1 parent
5b96dbe
commit 413fb8f
Showing
13 changed files
with
316 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/ApiHost1.ArchitectureTests/ApiHost1.ArchitectureTests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<IsTestProject>true</IsTestProject> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\ApiHost1\ApiHost1.csproj" /> | ||
<ProjectReference Include="..\ArchitectureTesting.Common\ArchitectureTesting.Common.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using ArchitectureTesting.Common; | ||
using Xunit; | ||
|
||
namespace ApiHost1.ArchitectureTests; | ||
|
||
[Trait("Category", "Unit.Architecture")] | ||
[Collection("Architecture")] | ||
public class HorizontalLayersSpec : HorizontalLayersSpecBase<Program> | ||
{ | ||
public HorizontalLayersSpec(ArchitectureSpecSetup<Program> setup) : base(setup) | ||
{ | ||
} | ||
|
||
//TODO: add additional tests here | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
using ArchitectureTesting.Common; | ||
using Xunit; | ||
|
||
namespace ApiHost1.ArchitectureTests; | ||
|
||
[CollectionDefinition("Architecture", DisableParallelization = true)] | ||
public class AllArchitectureSpecs : ICollectionFixture<ArchitectureSpecSetup<Program>>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using ArchitectureTesting.Common; | ||
using Xunit; | ||
|
||
namespace ApiHost1.ArchitectureTests; | ||
|
||
[Trait("Category", "Unit.Architecture")] | ||
[Collection("Architecture")] | ||
public class VerticalSlicesSpec : VerticalSlicesSpecBase<Program> | ||
{ | ||
public VerticalSlicesSpec(ArchitectureSpecSetup<Program> setup) : base(setup) | ||
{ | ||
} | ||
|
||
//TODO: add additional tests here | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using ArchUnitNET.Domain; | ||
using ArchUnitNET.Fluent; | ||
using ArchUnitNET.Fluent.Syntax.Elements.Types; | ||
using ArchUnitNET.Loader; | ||
|
||
namespace ArchitectureTesting.Common; | ||
|
||
// ReSharper disable once ClassNeverInstantiated.Global | ||
public class ArchitectureSpecSetup<THost> | ||
{ | ||
/// <summary> | ||
/// Provides a base class for all architecture tests, that works on all the assemblies that are loaded and reachable | ||
/// from the specified <see cref="THost" />. | ||
/// Note: We must NOT cache the properties like <see cref="DomainLayer" /> between tests. | ||
/// </summary> | ||
public ArchitectureSpecSetup() | ||
{ | ||
var hostAssembly = typeof(THost).Assembly; | ||
var assemblyLocation = Path.GetDirectoryName(hostAssembly.Location); | ||
|
||
Architecture = new ArchLoader() | ||
.LoadFilteredDirectory(assemblyLocation, "*.dll") | ||
.Build(); | ||
} | ||
|
||
public GivenTypesConjunctionWithDescription ApplicationLayer => ArchRuleDefinition.Types(true).That() | ||
.ResideInNamespace(ArchitectureTestingConstants.Layers.Application.SubdomainProjectNamespaces, true) | ||
.Or().ResideInNamespace(ArchitectureTestingConstants.Layers.Application.PlatformProjectNamespaces, true) | ||
.As(ArchitectureTestingConstants.Layers.Application.DisplayName); | ||
|
||
public Architecture Architecture { get; } | ||
|
||
public GivenTypesConjunctionWithDescription DomainLayer => ArchRuleDefinition.Types(true).That() | ||
.ResideInNamespace(ArchitectureTestingConstants.Layers.Domain.SubdomainProjectNamespaces, true) | ||
.Or().ResideInNamespace(ArchitectureTestingConstants.Layers.Domain.PlatformProjectNamespaces, true) | ||
.As(ArchitectureTestingConstants.Layers.Domain.DisplayName); | ||
|
||
public GivenTypesConjunctionWithDescription InfrastructureLayer => ArchRuleDefinition.Types(true).That() | ||
.ResideInNamespace(ArchitectureTestingConstants.Layers.Infrastructure.SubdomainProjectNamespaces, true) | ||
.Or().ResideInNamespace(ArchitectureTestingConstants.Layers.Infrastructure.PlatformProjectsNamespaces, true) | ||
.Or().ResideInNamespace(ArchitectureTestingConstants.Layers.Infrastructure.ApiHostProjectsNamespaces, true) | ||
.As(ArchitectureTestingConstants.Layers.Infrastructure.DisplayName); | ||
} |
18 changes: 18 additions & 0 deletions
18
src/ArchitectureTesting.Common/ArchitectureTesting.Common.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<IsTestProject>true</IsTestProject> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\UnitTesting.Common\UnitTesting.Common.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" /> | ||
<PackageReference Include="TngTech.ArchUnitNET" Version="0.10.5" /> | ||
<PackageReference Include="TngTech.ArchUnitNET.xUnit" Version="0.10.5" /> | ||
</ItemGroup> | ||
|
||
</Project> |
35 changes: 35 additions & 0 deletions
35
src/ArchitectureTesting.Common/ArchitectureTestingConstants.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
namespace ArchitectureTesting.Common; | ||
|
||
public static class ArchitectureTestingConstants | ||
{ | ||
public static class Layers | ||
{ | ||
public static class Domain | ||
{ | ||
public const string AllOthersLabel = "Other Subdomains' Domain Layer types"; | ||
public const string DisplayName = "Domain Layer"; | ||
public const string PlatformProjectNamespaces = $@"^{ProjectSuffix}[\w\.]*$"; | ||
public const string ProjectSuffix = "Domain"; | ||
public const string SubdomainProjectNamespaces = $@"^[\w]+{ProjectSuffix}[\w\.]*$"; | ||
} | ||
|
||
public static class Application | ||
{ | ||
public const string AllOthersLabel = "Other Subdomains' Application Layer types"; | ||
public const string DisplayName = "Application Layer"; | ||
public const string PlatformProjectNamespaces = $@"^{ProjectSuffix}[\w\.]*$"; | ||
public const string ProjectSuffix = "Application"; | ||
public const string SubdomainProjectNamespaces = $@"^[\w]+{ProjectSuffix}[\w\.]*$"; | ||
} | ||
|
||
public static class Infrastructure | ||
{ | ||
public const string AllOthersLabel = "Other Subdomains' Infrastructure Layer types"; | ||
public const string ApiHostProjectsNamespaces = @"^Api[\w]+Host[\w\.]*$"; | ||
public const string DisplayName = "Infrastructure Layer"; | ||
public const string PlatformProjectsNamespaces = $@"^{ProjectSuffix}[\w\.]*$"; | ||
public const string ProjectSuffix = "Infrastructure"; | ||
public const string SubdomainProjectNamespaces = $@"^[\w]+{ProjectSuffix}[\w\.]*$"; | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
src/ArchitectureTesting.Common/HorizontalLayersSpecBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using ArchUnitNET.Fluent; | ||
using ArchUnitNET.xUnit; | ||
using Xunit; | ||
|
||
namespace ArchitectureTesting.Common; | ||
|
||
/// <summary> | ||
/// Provides a base class for testing the horizontal layers of a specific <see cref="THost" /> | ||
/// </summary> | ||
public abstract class HorizontalLayersSpecBase<THost> | ||
{ | ||
protected readonly ArchitectureSpecSetup<THost> Setup; | ||
|
||
protected HorizontalLayersSpecBase(ArchitectureSpecSetup<THost> setup) | ||
{ | ||
Setup = setup; | ||
} | ||
|
||
[Fact] | ||
public void WhenAnyDomainLayerTypeDependsOnAnyApplicationLayerType_ThenFails() | ||
{ | ||
ArchRuleDefinition.Types().That().Are(Setup.DomainLayer) | ||
.Should().NotDependOnAny(Setup.ApplicationLayer) | ||
.Check(Setup.Architecture); | ||
} | ||
|
||
[Fact] | ||
public void WhenAnyDomainLayerTypeDependsOnAnyInfrastructureLayerType_ThenFails() | ||
{ | ||
ArchRuleDefinition.Types().That().Are(Setup.DomainLayer) | ||
.Should().NotDependOnAny(Setup.InfrastructureLayer) | ||
.Check(Setup.Architecture); | ||
} | ||
|
||
[Fact] | ||
public void WhenAnyApplicationLayerTypeDependsOnAnyInfrastructureLayerType_ThenFails() | ||
{ | ||
ArchRuleDefinition.Types().That().Are(Setup.ApplicationLayer) | ||
.Should().NotDependOnAny(Setup.InfrastructureLayer) | ||
.Check(Setup.Architecture); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using ArchUnitNET.Domain.Extensions; | ||
using ArchUnitNET.Fluent; | ||
using ArchUnitNET.xUnit; | ||
using Xunit; | ||
|
||
namespace ArchitectureTesting.Common; | ||
|
||
/// <summary> | ||
/// Provides a base class for testing each vertical slice (subdomain) of a specific <see cref="THost" /> | ||
/// </summary> | ||
public abstract class VerticalSlicesSpecBase<THost> | ||
{ | ||
private readonly List<string> _allSubdomainTypes; | ||
private readonly Func<string, string, string> _otherSubdomainProjectsNamespaceFormat = | ||
(domainName, prefix) => $@"^(((?!{domainName})[\w]+){prefix}((\.[\w]+)*))$"; | ||
private readonly Func<string, string, string> _subdomainProjectNamespaceFormat = | ||
(domainName, prefix) => $@"^{domainName}{prefix}((\.[\w]+)*)$"; | ||
protected readonly ArchitectureSpecSetup<THost> Setup; | ||
|
||
protected VerticalSlicesSpecBase(ArchitectureSpecSetup<THost> setup) | ||
{ | ||
Setup = setup; | ||
_allSubdomainTypes = Setup.Architecture.Namespaces | ||
.Where(ns => ns.NameEndsWith(ArchitectureTestingConstants.Layers.Domain.ProjectSuffix)) | ||
.Select(ns => | ||
ns.Name.Substring(0, | ||
ns.Name.IndexOf(ArchitectureTestingConstants.Layers.Domain.ProjectSuffix, | ||
StringComparison.Ordinal))) | ||
.Distinct() | ||
.ToList(); | ||
} | ||
|
||
[Fact] | ||
public void WhenAnyDomainTypeDependsOnAnotherSubdomainDomainType_ThenFails() | ||
{ | ||
_allSubdomainTypes.ForEach(domainName => | ||
{ | ||
var subdomainTypes = ArchRuleDefinition.Types().That() | ||
.ResideInNamespace( | ||
_subdomainProjectNamespaceFormat(domainName, | ||
ArchitectureTestingConstants.Layers.Domain.ProjectSuffix), true) | ||
.As($"{domainName}{ArchitectureTestingConstants.Layers.Domain.ProjectSuffix}"); | ||
var anyOtherSubdomainType = ArchRuleDefinition.Types().That() | ||
.ResideInNamespace(_otherSubdomainProjectsNamespaceFormat(domainName, | ||
ArchitectureTestingConstants.Layers.Domain.ProjectSuffix), true) | ||
.As(ArchitectureTestingConstants.Layers.Domain.AllOthersLabel); | ||
|
||
ArchRuleDefinition.Types().That().Are(subdomainTypes) | ||
.Should().NotDependOnAny(anyOtherSubdomainType) | ||
.Check(Setup.Architecture); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public void WhenAnyApplicationTypeDependsOnAnotherSubdomainApplicationType_ThenFails() | ||
{ | ||
_allSubdomainTypes.ForEach(domainName => | ||
{ | ||
var subdomainTypes = ArchRuleDefinition.Types().That() | ||
.ResideInNamespace( | ||
_subdomainProjectNamespaceFormat(domainName, | ||
ArchitectureTestingConstants.Layers.Application.ProjectSuffix), true) | ||
.As($"{domainName}{ArchitectureTestingConstants.Layers.Application.ProjectSuffix}"); | ||
var anyOtherSubdomainType = ArchRuleDefinition.Types().That() | ||
.ResideInNamespace(_otherSubdomainProjectsNamespaceFormat(domainName, | ||
ArchitectureTestingConstants.Layers.Application.ProjectSuffix), true) | ||
.As(ArchitectureTestingConstants.Layers.Application.AllOthersLabel); | ||
|
||
ArchRuleDefinition.Types().That().Are(subdomainTypes) | ||
.Should().NotDependOnAny(anyOtherSubdomainType) | ||
.Check(Setup.Architecture); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public void WhenAnyInfrastructureTypeDependsOnAnotherSubdomainInfrastructureType_ThenFails() | ||
{ | ||
_allSubdomainTypes.ForEach(domainName => | ||
{ | ||
var subdomainTypes = ArchRuleDefinition.Types().That() | ||
.ResideInNamespace( | ||
_subdomainProjectNamespaceFormat(domainName, | ||
ArchitectureTestingConstants.Layers.Infrastructure.ProjectSuffix), true) | ||
.As($"{domainName}{ArchitectureTestingConstants.Layers.Infrastructure.ProjectSuffix}"); | ||
var anyOtherSubdomainType = ArchRuleDefinition.Types().That() | ||
.ResideInNamespace(_otherSubdomainProjectsNamespaceFormat(domainName, | ||
ArchitectureTestingConstants.Layers.Infrastructure.ProjectSuffix), true) | ||
.As(ArchitectureTestingConstants.Layers.Infrastructure.AllOthersLabel); | ||
|
||
ArchRuleDefinition.Types().That().Are(subdomainTypes) | ||
.Should().NotDependOnAny(anyOtherSubdomainType) | ||
.Check(Setup.Architecture); | ||
}); | ||
} | ||
} |
Oops, something went wrong.