From 022f2f4aa54adf8f8d7cacad74df70c3d661d683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Sat, 6 Jun 2026 22:57:46 +0200 Subject: [PATCH 1/2] docs: show verified failure messages for all three libraries in the comparison page --- .../comparison/02-architecture-comparison.mdx | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/Docs/pages/comparison/02-architecture-comparison.mdx b/Docs/pages/comparison/02-architecture-comparison.mdx index e9ee6e1d..73eedd3f 100644 --- a/Docs/pages/comparison/02-architecture-comparison.mdx +++ b/Docs/pages/comparison/02-architecture-comparison.mdx @@ -22,7 +22,8 @@ approaches against each other. ```csharp await Expect.That(Types.InNamespace("MyApp.Presentation")) - .DoNotDependOn("MyApp.Data"); + .DoNotDependOn("MyApp.Data") + .Because("the presentation layer is decoupled from persistence"); ``` No separate loader or result handling: the selection scans the loaded assemblies, and a violation fails @@ -162,31 +163,55 @@ type-to-type references behind each edge. ## Failure messages -What you see when a rule is violated: +What failure of [the rule from the top of this page](#the-same-rule-in-all-three-libraries) looks like in +each library, when `OrderService` in the presentation layer references `OrderRepository` in the data layer: -- **NetArchTest** returns the failing type names, and nothing else. The test failure message is whatever - you build out of `result.FailingTypeNames`; there is no description of *which rule* failed or *why* a - type matched. -- **ArchUnitNET** throws an exception whose message names the rule (including a custom `Because(…)` reason) - and lists each violating object with a failure description. -- **aweXpect.Reflection** reports every violation with the expectation text, and - [`Expect.ThatAll`](../03-architecture-rules.md#layers-as-type-selections) groups several rules into a - single verification that lists all failures together: + + ``` -Expected all of the following to succeed: - [01] Expected that domain all do not depend on types within namespace "MyApp.Infrastructure" in all loaded assemblies - [02] Expected that domain are all sealed -but - [01] it contained types with the dependency [ +Expected that types within namespace "MyApp.Presentation" in all loaded assemblies +all do not depend on namespace "MyApp.Data", because the presentation layer is decoupled from persistence, +but it contained types with the dependency [ OrderService ] - [02] it contained non-sealed types [ - Order, - Invoice -] ``` +The selection and the rule describe themselves in the message, including the `Because(…)` reason, +followed by the violating types. + + + + +``` +Assert.True() Failure +Expected: True +Actual: False +``` + +Neither the rule nor the violators are part of the message: `Assert.True(result.IsSuccessful)` reports +only the boolean (and a custom user message replaces even that). A useful failure message has to be built +by hand from `result.FailingTypeNames`. + + + + +``` +"Types that reside in namespace with full name "MyApp.Presentation" should not depend on any Types that reside in namespace with full name "MyApp.Data" because the presentation layer is decoupled from persistence" failed: + MyApp.Presentation.OrderService does depend on "MyApp.Data.OrderRepository" +``` + +The `FailedArchRuleException` message quotes the rule, including the `Because(…)` reason, and lists each +violating object with a failure description. + + + + +Several aweXpect rules combine into one verification with `Expect.ThatAll(…)`, which numbers each rule +and reports all failures together; see +[layers as type selections](../03-architecture-rules.md#layers-as-type-selections) for a full example +with output. + ## Beyond architecture rules NetArchTest stops at types, and ArchUnitNET's member rules cover names, visibility and a few modifiers. From 11835be9cf8c43ed6708cf77cf3c975e0857d50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Sat, 6 Jun 2026 23:04:54 +0200 Subject: [PATCH 2/2] docs: rename layer selection variables to domainTypes/infrastructureTypes/repositoryTypes --- Docs/pages/03-architecture-rules.md | 26 +++++++++++++------------- README.md | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Docs/pages/03-architecture-rules.md b/Docs/pages/03-architecture-rules.md index c00dc007..2a01475f 100644 --- a/Docs/pages/03-architecture-rules.md +++ b/Docs/pages/03-architecture-rules.md @@ -174,9 +174,9 @@ There is no separate rule engine: a "layer" is just a reusable `Filtered.Types` filter vocabulary at your disposal), and an architecture rule is just an expectation on it. ```csharp -Filtered.Types domain = Types.InNamespace("MyApp.Domain"); -Filtered.Types infrastructure = Types.InNamespace("MyApp.Infrastructure"); -Filtered.Types repositories = Types.InNamespace("MyApp.Data").WithName("Repository").AsSuffix(); +Filtered.Types domainTypes = Types.InNamespace("MyApp.Domain"); +Filtered.Types infrastructureTypes = Types.InNamespace("MyApp.Infrastructure"); +Filtered.Types repositoryTypes = Types.InNamespace("MyApp.Data").WithName("Repository").AsSuffix(); ``` The dependency assertions and filters accept such a selection as a **target**, alongside the namespace and @@ -195,13 +195,13 @@ matched normally by `DependsOn` / `DoesNotDependOn`. ```csharp // Outgoing rule with a selection as target: -await Expect.That(domain).DoNotDependOn(infrastructure); +await Expect.That(domainTypes).DoNotDependOn(infrastructureTypes); // Incoming rules are written explicitly from the other side: -await Expect.That(infrastructure).DoNotDependOn(domain); +await Expect.That(infrastructureTypes).DoNotDependOn(domainTypes); // Allowed set as union of selections (own namespace + framework stay allowed): -await Expect.That(domain).DependOnlyOn(repositories).OrOn(infrastructure); +await Expect.That(domainTypes).DependOnlyOn(repositoryTypes).OrOn(infrastructureTypes); ``` Combine several rules into a single verification with aweXpect's `Expect.ThatAll(…)` (see @@ -211,17 +211,17 @@ dependency ones, so naming conventions or sealing rules live in the same check: ```csharp await Expect.ThatAll( - Expect.That(domain).DoNotDependOn(infrastructure), - Expect.That(domain).DependOnlyOn(repositories).OrOn(infrastructure), - Expect.That(domain).AreSealed()); + Expect.That(domainTypes).DoNotDependOn(infrastructureTypes), + Expect.That(domainTypes).DependOnlyOn(repositoryTypes).OrOn(infrastructureTypes), + Expect.That(domainTypes).AreSealed()); ``` A failing rule reports all violations, numbered per expectation: ``` Expected all of the following to succeed: - [01] Expected that domain all do not depend on types within namespace "MyApp.Infrastructure" in all loaded assemblies - [02] Expected that domain are all sealed + [01] Expected that domainTypes all do not depend on types within namespace "MyApp.Infrastructure" in all loaded assemblies + [02] Expected that domainTypes are all sealed but [01] it contained types with the dependency [ OrderService @@ -235,8 +235,8 @@ but Exemptions to a rule use the [`Except` filter](./02-filters.md) on the subject selection: ```csharp -await Expect.That(domain.Except()).DoNotDependOn(infrastructure); -await Expect.That(domain.Except(type => type.Name.StartsWith("Generated"))).AreSealed(); +await Expect.That(domainTypes.Except()).DoNotDependOn(infrastructureTypes); +await Expect.That(domainTypes.Except(type => type.Name.StartsWith("Generated"))).AreSealed(); ``` A layer spanning several namespaces is built by widening a dependency *target* with additional selections diff --git a/README.md b/README.md index 5f15d4d1..3c7745a1 100644 --- a/README.md +++ b/README.md @@ -89,20 +89,20 @@ There is no separate rule engine: a "layer" is just a reusable type selection, a just an expectation on it. Combine several rules into a single verification with `Expect.ThatAll`: ```csharp -Filtered.Types domain = Types.InNamespace("MyApp.Domain"); -Filtered.Types infrastructure = Types.InNamespace("MyApp.Infrastructure"); +Filtered.Types domainTypes = Types.InNamespace("MyApp.Domain"); +Filtered.Types infrastructureTypes = Types.InNamespace("MyApp.Infrastructure"); await Expect.ThatAll( - Expect.That(domain).DoNotDependOn(infrastructure), - Expect.That(domain).AreSealed()); + Expect.That(domainTypes).DoNotDependOn(infrastructureTypes), + Expect.That(domainTypes).AreSealed()); ``` A failing rule reports all violations, numbered per expectation: ``` Expected all of the following to succeed: - [01] Expected that domain all do not depend on types within namespace "MyApp.Infrastructure" in all loaded assemblies - [02] Expected that domain are all sealed + [01] Expected that domainTypes all do not depend on types within namespace "MyApp.Infrastructure" in all loaded assemblies + [02] Expected that domainTypes are all sealed but [01] it contained types with the dependency [ OrderService