From f1fab71d244781264399ab64c9436ec0b926f914 Mon Sep 17 00:00:00 2001 From: Meikel Philipp Date: Thu, 7 Aug 2025 12:57:53 +0200 Subject: [PATCH] feat: add TUnit --- ArchUnit.sln | 12 +++++ ArchUnitNET.TUnit/ArchRuleAssert.cs | 23 +++++++++ ArchUnitNET.TUnit/ArchRuleExtensions.cs | 29 +++++++++++ ArchUnitNET.TUnit/ArchUnitNET.TUnit.csproj | 41 ++++++++++++++++ ArchUnitNET.TUnit/FailedArchRuleException.cs | 27 ++++++++++ .../ArchUnitNET.TUnitTests.csproj | 15 ++++++ ArchUnitNET.TUnitTests/RuleEvaluationTests.cs | 49 +++++++++++++++++++ 7 files changed, 196 insertions(+) create mode 100644 ArchUnitNET.TUnit/ArchRuleAssert.cs create mode 100644 ArchUnitNET.TUnit/ArchRuleExtensions.cs create mode 100644 ArchUnitNET.TUnit/ArchUnitNET.TUnit.csproj create mode 100644 ArchUnitNET.TUnit/FailedArchRuleException.cs create mode 100644 ArchUnitNET.TUnitTests/ArchUnitNET.TUnitTests.csproj create mode 100644 ArchUnitNET.TUnitTests/RuleEvaluationTests.cs diff --git a/ArchUnit.sln b/ArchUnit.sln index c2d45656..9d640992 100644 --- a/ArchUnit.sln +++ b/ArchUnit.sln @@ -40,6 +40,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchUnitNET.xUnitTests", "A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchUnitNET.xUnitV3Tests", "ArchUnitNET.xUnitV3Tests\ArchUnitNET.xUnitV3Tests.csproj", "{439E00CE-5CB8-4474-9A27-3FFC5770DB7C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchUnitNET.TUnit", "ArchUnitNET.TUnit\ArchUnitNET.TUnit.csproj", "{AD4D9490-F6F1-47DC-9F52-FD15497CDE55}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchUnitNET.TUnitTests", "ArchUnitNET.TUnitTests\ArchUnitNET.TUnitTests.csproj", "{C54143CE-3256-4313-B991-0706705EEA1D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -118,6 +122,14 @@ Global {439E00CE-5CB8-4474-9A27-3FFC5770DB7C}.Debug|Any CPU.Build.0 = Debug|Any CPU {439E00CE-5CB8-4474-9A27-3FFC5770DB7C}.Release|Any CPU.ActiveCfg = Release|Any CPU {439E00CE-5CB8-4474-9A27-3FFC5770DB7C}.Release|Any CPU.Build.0 = Release|Any CPU + {AD4D9490-F6F1-47DC-9F52-FD15497CDE55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD4D9490-F6F1-47DC-9F52-FD15497CDE55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD4D9490-F6F1-47DC-9F52-FD15497CDE55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD4D9490-F6F1-47DC-9F52-FD15497CDE55}.Release|Any CPU.Build.0 = Release|Any CPU + {C54143CE-3256-4313-B991-0706705EEA1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C54143CE-3256-4313-B991-0706705EEA1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C54143CE-3256-4313-B991-0706705EEA1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C54143CE-3256-4313-B991-0706705EEA1D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ArchUnitNET.TUnit/ArchRuleAssert.cs b/ArchUnitNET.TUnit/ArchRuleAssert.cs new file mode 100644 index 00000000..b01752b9 --- /dev/null +++ b/ArchUnitNET.TUnit/ArchRuleAssert.cs @@ -0,0 +1,23 @@ +using ArchUnitNET.Domain; +using ArchUnitNET.Fluent; + +// ReSharper disable once CheckNamespace +namespace ArchUnitNET.TUnit +{ + public static class ArchRuleAssert + { + /// + /// Verifies that the architecture meets the criteria of the archrule. + /// + /// The architecture to be tested + /// The rule to test the architecture with + public static void CheckRule(Architecture architecture, IArchRule archRule) + { + if (!archRule.HasNoViolations(architecture)) + { + var results = archRule.Evaluate(architecture); + throw new FailedArchRuleException(architecture, archRule); + } + } + } +} diff --git a/ArchUnitNET.TUnit/ArchRuleExtensions.cs b/ArchUnitNET.TUnit/ArchRuleExtensions.cs new file mode 100644 index 00000000..bbe8fbc2 --- /dev/null +++ b/ArchUnitNET.TUnit/ArchRuleExtensions.cs @@ -0,0 +1,29 @@ +using ArchUnitNET.Domain; +using ArchUnitNET.Fluent; + +// ReSharper disable once CheckNamespace +namespace ArchUnitNET.TUnit +{ + public static class ArchRuleExtensions + { + /// + /// Verifies that the architecture meets the criteria of the archrule. + /// + /// The rule to test the architecture with + /// The architecture to be tested + public static void Check(this IArchRule archRule, Architecture architecture) + { + ArchRuleAssert.CheckRule(architecture, archRule); + } + + /// + /// Verifies that the architecture meets the criteria of the archrule. + /// + /// The architecture to be tested + /// The rule to test the architecture with + public static void CheckRule(this Architecture architecture, IArchRule archRule) + { + ArchRuleAssert.CheckRule(architecture, archRule); + } + } +} diff --git a/ArchUnitNET.TUnit/ArchUnitNET.TUnit.csproj b/ArchUnitNET.TUnit/ArchUnitNET.TUnit.csproj new file mode 100644 index 00000000..4dc20a37 --- /dev/null +++ b/ArchUnitNET.TUnit/ArchUnitNET.TUnit.csproj @@ -0,0 +1,41 @@ + + + netstandard2.0;net472 + latest + true + ArchUnit C# TUnit Extension + TUnit Extension for the C# Version of ArchUnit (see: archunit.org) + Apache-2.0 + https://github.com/TNG/ArchUnitNET + test;arch;archunit;tunit + False + TNG Technology Consulting GmbH + TngTech.ArchUnitNET.TUnit + false + true + snupkg + README.md + true + ArchUnitNET.TUnit + + + + + + + + + + + + true + /Logo/ + ArchUnitNET-Logo.png + + + true + + README.md + + + diff --git a/ArchUnitNET.TUnit/FailedArchRuleException.cs b/ArchUnitNET.TUnit/FailedArchRuleException.cs new file mode 100644 index 00000000..5e18b0ee --- /dev/null +++ b/ArchUnitNET.TUnit/FailedArchRuleException.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using ArchUnitNET.Domain; +using ArchUnitNET.Fluent; +using ArchUnitNET.Fluent.Extensions; +using TUnit.Assertions.Exceptions; + +// ReSharper disable once CheckNamespace +namespace ArchUnitNET.TUnit +{ + public class FailedArchRuleException : BaseAssertionException + { + /// + /// Creates a new instance of the class. + /// + /// The architecture which was tested + /// The archrule that failed + public FailedArchRuleException(Architecture architecture, IArchRule archRule) + : this(archRule.Evaluate(architecture)) { } + + /// + /// Creates a new instance of the class. + /// + /// The results of the evaluation of the archrule + public FailedArchRuleException(IEnumerable evaluationResults) + : base(evaluationResults.ToErrorMessage()) { } + } +} diff --git a/ArchUnitNET.TUnitTests/ArchUnitNET.TUnitTests.csproj b/ArchUnitNET.TUnitTests/ArchUnitNET.TUnitTests.csproj new file mode 100644 index 00000000..13d1be19 --- /dev/null +++ b/ArchUnitNET.TUnitTests/ArchUnitNET.TUnitTests.csproj @@ -0,0 +1,15 @@ + + + net9.0 + false + TNG Technology Consulting GmbH + true + + + + + + + + + diff --git a/ArchUnitNET.TUnitTests/RuleEvaluationTests.cs b/ArchUnitNET.TUnitTests/RuleEvaluationTests.cs new file mode 100644 index 00000000..bb848a46 --- /dev/null +++ b/ArchUnitNET.TUnitTests/RuleEvaluationTests.cs @@ -0,0 +1,49 @@ +using ArchUnitNET.Domain; +using ArchUnitNET.Fluent; +using ArchUnitNET.Fluent.Extensions; +using ArchUnitNET.Loader; +using ArchUnitNET.TUnit; +using static ArchUnitNET.Fluent.ArchRuleDefinition; +using System.Threading.Tasks; + +namespace ArchUnitNET.TUnitTests +{ + public class RuleEvaluationTests + { + private readonly Architecture _architecture; + private readonly string _expectedErrorMessage; + private readonly IArchRule _falseRule; + private readonly IArchRule _trueRule; + + public RuleEvaluationTests() + { + _architecture = new ArchLoader() + .LoadAssemblies(System.Reflection.Assembly.Load("ArchUnitNET.TUnitTests")) + .Build(); + _trueRule = Classes().That().Are(typeof(RuleEvaluationTests)).Should().Exist(); + _falseRule = Classes().That().Are(typeof(RuleEvaluationTests)).Should().NotExist(); + _expectedErrorMessage = _falseRule.Evaluate(_architecture).ToErrorMessage(); + } + + [Test] + public async Task ArchRuleAssertTest() + { + ArchRuleAssert.CheckRule(_architecture, _trueRule); + var ex = Assert.Throws(() => + ArchRuleAssert.CheckRule(_architecture, _falseRule) + ); + await Assert.That(ex.Message).IsEqualTo(_expectedErrorMessage); + } + + [Test] + public async Task ArchRuleExtensionsTest() + { + _architecture.CheckRule(_trueRule); + _trueRule.Check(_architecture); + var ex1 = Assert.Throws(() => _architecture.CheckRule(_falseRule)); + var ex2 = Assert.Throws(() => _falseRule.Check(_architecture)); + await Assert.That(ex1.Message).IsEqualTo(_expectedErrorMessage); + await Assert.That(ex2.Message).IsEqualTo(_expectedErrorMessage); + } + } +}