{"id":37034273,"url":"https://github.com/nevespl/netarchtest.enhancededition","last_synced_at":"2026-01-14T04:01:14.479Z","repository":{"id":60786252,"uuid":"543449771","full_name":"NeVeSpl/NetArchTest.eNhancedEdition","owner":"NeVeSpl","description":"A fluent API for .Net that can enforce architectural rules in unit tests. ","archived":false,"fork":false,"pushed_at":"2025-06-04T19:09:51.000Z","size":625,"stargazers_count":46,"open_issues_count":1,"forks_count":4,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-23T15:54:51.114Z","etag":null,"topics":["archtest","archunit","cecil","netarchtest"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NeVeSpl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-09-30T05:54:17.000Z","updated_at":"2025-09-15T16:55:40.000Z","dependencies_parsed_at":"2023-11-19T18:45:08.650Z","dependency_job_id":"0f58a595-fb29-4140-a11c-62b71b179e8c","html_url":"https://github.com/NeVeSpl/NetArchTest.eNhancedEdition","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/NeVeSpl/NetArchTest.eNhancedEdition","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NeVeSpl%2FNetArchTest.eNhancedEdition","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NeVeSpl%2FNetArchTest.eNhancedEdition/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NeVeSpl%2FNetArchTest.eNhancedEdition/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NeVeSpl%2FNetArchTest.eNhancedEdition/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NeVeSpl","download_url":"https://codeload.github.com/NeVeSpl/NetArchTest.eNhancedEdition/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NeVeSpl%2FNetArchTest.eNhancedEdition/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["archtest","archunit","cecil","netarchtest"],"created_at":"2026-01-14T04:01:13.863Z","updated_at":"2026-01-14T04:01:14.472Z","avatar_url":"https://github.com/NeVeSpl.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿[![net-workflow](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/actions/workflows/net-workflow.yml/badge.svg?branch=main)](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/actions/workflows/net-workflow.yml)\n[![Nuget](https://img.shields.io/nuget/v/NetArchTest.eNhancedEdition?color=%23004880\u0026label=NetArchTest.eNhancedEdition%20nuget)](https://www.nuget.org/packages/NetArchTest.eNhancedEdition)\n\n# NetArchTest.eNhancedEdition\n\nA fluent API for .Net Standard that can enforce architectural rules in unit tests and create a *self-testing architecture*. Inspired by the [ArchUnit](https://www.archunit.org/) library for Java.\n\nNetArchTest.eNhancedEdition is based on [NetArchTest v1.3.2](https://github.com/BenMorris/NetArchTest). If you are not familiar with NetArchTest, you should start by reading [introduction on Ben's blog](https://www.ben-morris.com/writing-archunit-style-tests-for-net-and-c-for-self-testing-architectures).\n\n### Rationale\n\nNetArchTest is a well-established mature library, but to push things forward, a few breaking changes had to be made, and that is how  **eNhancedEdition** was born. eNhancedEdition uses almost identical Fluent API as a base library, but it is not 100% backwards compatible, and it will never be. \n\nWhat **eNhancedEdition** has to offer, that is not available in the NetArchTest v1.3.2:\n - fixed all known bugs present in v1.3.2 :\n     - BenMorris/NetArchTest#98,\n     - BenMorris/NetArchTest#101,\n     - BenMorris/NetArchTest#120,\n     - NeVeSpl/NetArchTest.eNhancedEdition#3\n - corrected design mistakes:\n     - BenMorris/NetArchTest#119 - fixed nulls for Success result\n     - BenMorris/NetArchTest#130 - for generic type, number of type parameters (e.g. `1) is no longer considered as a part of its name\n - added new features:\n     - [Slices](#slices)\n     - BenMorris/NetArchTest#67 - added rules: AreOfType, AreNotOfType\n     - BenMorris/NetArchTest#97 - added rules: HaveSourceFileNameMatchingName, HaveSourceFilePathMatchingNamespace\n     - BenMorris/NetArchTest#100 - added rules: AreImmutable, AreImmutableExternally, AreStateless\n     - BenMorris/NetArchTest#104 - added rule: HaveMatchingTypeWithName\n     - BenMorris/NetArchTest#105 - dependency search functions: HaveDependencyOnAny/OnlyHaveDependencyOn explain why a type fails test through [IType.Explanation](documentation/api.md#itypeexplanation) \n     - BenMorris/NetArchTest#126 - added rules for structs, enums and delegates\n     - BenMorris/NetArchTest#131 - added rules for all access modifiers: public, internal, private, protected, private protected, protected internal\n     - BenMorris/NetArchTest#133 - added rules: AreInheritedByAnyType, AreNotInheritedByAnyType\n     - added rules: AreUsedByAny, AreNotUsedByAny\n  - at the end, you get more information which should make reasoning about tests easier:\n \n\n![revit-database-scripting-update-query](https://raw.githubusercontent.com/NeVeSpl/NetArchTest.eNhancedEdition/main/documentation/result.printscreen.png)\n\n\n\n## Index\n\n* [Getting started](#getting-started)\n    * [Examples](#examples)\n    * [Writing rules](#writing-rules)\n* [Rules for dependency analysis](#rules-for-dependency-analysis)\n* [Rules for assessing design and architectural principles](#rules-for-assessing-design-and-architectural-principles)\n* [Slices](#slices)\n* [Custom rules](#custom-rules)\n* [Options](#options)\n* [Limitations](#limitations)\n* API\n   * [Types](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#types)\n   * [Predicate](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#predicate)\n   * [PredicateList](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#predicateList)\n   * [Condition](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#condition)\n   * [ConditionList](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#conditionList)\n   * [TestResult](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#testResult)\n   * [IType](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#itype)\n   * [Options](https://github.com/NeVeSpl/NetArchTest.eNhancedEdition/blob/main/documentation/api.md#options)\n\n\n\n## Getting started\n\nThe library is available as a package on NuGet: [NetArchTest.eNhancedEdition](https://www.nuget.org/packages/NetArchTest.eNhancedEdition/).\n\n### Examples\n\n```csharp\n[TestClass]\npublic class SampleApp_ModuleAlpha_Tests\n{\n    static readonly Assembly AssemblyUnderTest = typeof(TestUtils).Assembly;\n\n    [TestMethod]\n    public void PersistenceIsNotAccessibleFromOutsideOfModuleExceptOfDbContext()\n    {\n        var result = Types.InAssembly(AssemblyUnderTest)\n                          .That()\n                          .ResideInNamespace(\"SampleApp.ModuleAlpha.Persistence\")\n                          .And()\n                          .DoNotHaveNameEndingWith(\"DbContext\")\n                          .Should()\n                          .NotBePublic()\n                          .GetResult();\n\n        Assert.IsTrue(result.IsSuccessful);\n    }\n\n    [TestMethod]\n    public void DomainIsIndependent()\n    {\n        var result = Types.InAssembly(AssemblyUnderTest)\n                          .That()\n                          .ResideInNamespace(\"SampleApp.ModuleAlpha.Domain\")\n                          .ShouldNot()\n                          .HaveDependencyOtherThan(\n                            \"System\",\n                            \"SampleApp.ModuleAlpha.Domain\",\n                            \"SampleApp.SharedKernel.Domain\",\n                            \"SampleApp.BuildingBlocks.Domain\"\n                          )\n                          .GetResult();\n\n        Assert.IsTrue(result.IsSuccessful, \"Domain has lost its independence!\");\n    }\n\n}\n\n[TestClass]\npublic class SampleApp_ModuleOmega_Tests\n{\n    static readonly Assembly AssemblyUnderTest = typeof(TestUtils).Assembly;\n\n    [TestMethod]\n    public void RequestHandlersShouldBeSealed()\n    {            \n        var result = Types.InAssembly(AssemblyUnderTest)\n                          .That()\n                          .ImplementInterface(typeof(IRequestHandler\u003c,\u003e))\n                          .Should()\n                          .BeSealed()\n                          .GetResult();\n\n        Assert.IsTrue(result.IsSuccessful);\n    }\n}\n```\n\n\n\n### Writing rules\n\nThe fluent API should direct you in building up a rule, based on a combination of [predicates](documentation/api.md#predicate), [conditions](documentation/api.md#condition) and conjunctions. \n\nThe starting point for any rule is one of the static methods on [`Types`](documentation/api.md#types) class, where you load a set of types from an assembly, domain or path.\n\n```csharp\nvar types = Types.InAssembly(typeof(MyClass).Assembly);\n```\nOnce you have loaded the types, you can filter them using one or more predicates. These can be chained together using `And()` or `Or()` conjunctions:\n```csharp\ntypes.That().ResideInNamespace(\"MyProject.Data\");\n```\nOnce the set of  types has been filtered, you can apply a set of conditions using the `Should()` or `ShouldNot()` methods, e.g.\n```csharp\ntypes.That().ResideInNamespace(\"MyProject.Data\").Should().BeSealed();\n```\nFinally, you obtain a result from the rule by using an executor, i.e. use `GetTypes()` to return the types that match the rule or `GetResult()` to determine whether the rule has been met.\n\nNote that `GetResult()` returns [`TestResult`](documentation/api.md#testresult) which contains a few lists of types:\n- `LoadedTypes` - all types loaded by [`Types`](documentation/api.md#types)\n- `SelectedTypesForTesting` - types that passed [predicates](documentation/api.md#predicate)\n- `FailingTypes`- types that failed to meet the [conditions](documentation/api.md#condition)\n\n```csharp\nvar result = types.That().ResideInNamespace(\"MyProject.Data\").Should().BeSealed().GetResult();\nvar isValid = result.IsSuccessful;\nvar types = result.FailingTypes;\n```\n\n\u003e **Tip**\nLoading types is time-consuming, since `Type` class is immutable, its instance can be shared between tests.\n\n## Rules for dependency analysis\n\nDependency matrix:\n\n| type\\has dependency on | D1 | D2 | D3 |\n|---|----|----|----|\n| a |    |    |    |\n| b |    |    | x  |\n| c |    | x  |    |\n| d |    | x  | x  |\n| e | x  |    |    |\n| f | x  |    | x  |\n| g | x  | x  |    |\n| h | x  | x  | x  |\n\n\n#### Dependency search:\n\n|   | Rule   | number\u003cbr\u003e of required\u003cbr\u003e dependencies \u003cbr\u003efrom the list  | type can have\u003cbr\u003ea dependency\u003cbr\u003ethat is not\u003cbr\u003eon the list  |  passing types |  failing types |\n|---|---|---|---|---|---|\n| 1 | [HaveDependencyOnAny(D1, D2)](documentation/api.md#conditionhavedependencyonany) | at least 1  | yes  |  c, d, e, f, g, h, |  a, b |\n| 2 | [HaveDependencyOnAll(D1, D2)](documentation/api.md#conditionhavedependencyonall)  | all  |  yes |  g, h | a, b, c, d, e, f  |\n| 3 | [OnlyHaveDependencyOn(D1, D2)](documentation/api.md#conditiononlyhavedependencyon) | \u003e=0  |  no | a, c, e, g  |  b, d, f, h  |\n| 1N | [NotHaveDependencyOnAny(D1, D2)](documentation/api.md#conditionnothavedependencyonany) | none  | yes   |  a, b |   c, d, e, f, g, h, |\n| 2N | [NotHaveDependencyOnAll(D1, D2)](documentation/api.md#conditionnothavedependencyonall)  | not all  |  yes | a, b, c, d, e, f |  g, h   |\n| 3N | [HaveDependencyOtherThan(D1, D2)](documentation/api.md#conditionhavedependencyotherthan)  |  \u003e=0 |  yes |  b, d, f, h, |  a, c, e, g  |\n\nAn explanation of why a type fails the dependency search test is available on the failing type: [IType.Explanation](documentation/api.md#itypeexplanation)\n\n\n#### Reverse dependency search\n\n|   | Predicate   | number\u003cbr\u003e of required\u003cbr\u003e dependencies \u003cbr\u003efrom the list  | type can use\u003cbr\u003ea type\u003cbr\u003ethat is not\u003cbr\u003eon the list  |  passing types |  failing types |\n|---|---|---|---|---|---|\n| R1 | [AreUsedByAny(c, d)](documentation/api.md#predicateareusedbyany) | at least 1  | yes  |  D2, D3 |  D1 |\n| R1N | [AreNotUsedByAny(c, d)](documentation/api.md#predicatearenotusedbyany)  | none  |  yes |  D1 |  D2, D3  |\n\n\n## Rules for assessing design and architectural principles\n\n#### BeImmutable\n\nA Type is considered as immutable when all its state (instance and static, fields, properties and events) cannot be changed after creation. Shallow immutability.\n\n#### BeImmutableExternally\n\nA Type is considered as externally immutable when its state (instance and static, fields, properties and events) with a public access modifier cannot be changed from the outside of the type. Shallow immutability.\n\n#### BeStateless\n\nA Type is considered as stateless when it does not have an instance state (fields, properties and events).\n\n#### BeStaticless\n\nA Type is considered as stateless when it does not have a static state.\n\n#### HaveParameterlessConstructor\n\nA type should have a parameterless instance constructor. \n\n#### DoNotHavePublicConstructor\n\nA type should not have any instance public constructors. \n\n#### HaveSourceFileNameMatchingName\n\n#### HaveSourceFilePathMatchingNamespace\n\n#### HaveMatchingTypeWithName\n\n\n\n## Slices \n\n```csharp\nvar result = Types.InAssembly(typeof(ExampleDependency).Assembly)\n                  .Slice()\n                  .ByNamespacePrefix(\"MyApp.Features\")\n                  .Should()\n                  .NotHaveDependenciesBetweenSlices()\n                  .GetResult();\n\n```\n\nThere is only one way, at least for now, to divide types into slices `ByNamespacePrefix(string prefix)` and it works as follows:\n\n1) Select types which namespace starts with a given prefix, rest of the types are ignored.\n2) Slices are defined by the first part of the namespace that comes right after the prefix:\n`namespacePrefix.(sliceName).restOfNamespace`\n3) Types with the same `sliceName` part will be placed in the same slice. If `sliceName` is empty for a given type, the type will also be ignored (`BaseFeature` class from the following image)\n\n![Slices](https://raw.githubusercontent.com/NeVeSpl/NetArchTest.eNhancedEdition/main/documentation/slices/slices.png)\n\nWhen our types are divided into slices, we can apply the condition: `NotHaveDependenciesBetweenSlices()`. As the name suggests it detects if any dependency exists between slices. Dependency from slice to type that is not part of any other slice is allowed.\n\npassing | failing\n--|---\n![Slices](https://raw.githubusercontent.com/NeVeSpl/NetArchTest.eNhancedEdition/main/documentation/slices/slices.ok.png)|![Slices](https://raw.githubusercontent.com/NeVeSpl/NetArchTest.eNhancedEdition/main/documentation/slices/slices.not.png)\n\n\n## Custom rules\n\nYou can extend the library by writing custom rules that implement the `ICustomRule` interface. These can be applied as both predicates and conditions using a `MeetsCustomRule()` method, e.g.\n\n```csharp\nvar myRule = new CustomRule();\n\n// Write your own custom rules that can be used as both predicates and conditions\nvar result = Types.InCurrentDomain()\n    .That()\n    .AreClasses()\n    .Should()\n    .MeetCustomRule(myRule)\n    .GetResult()\n    .IsSuccessful;\n```\n\n\n## Options\n\nUser [Options](documentation/api.md#options-1) allows to configure how NetArchTest engine works.\n\n```csharp\nvar result = Types.InCurrentDomain()\n    .That()\n    .ResideInNamespace(\"NetArchTest.TestStructure.NameMatching.Namespace3\")\n    .Should()\n    .HaveNameStartingWith(\"Some\")\n    .GetResult(Options.Default with { Comparer = StringComparison.Ordinal});\n\nAssert.True(result.IsSuccessful);\n```\n\nAvailable options:\n- [Comparer](documentation/api.md#optionscomparer) - allows to specify how strings will be compared, default: InvariantCultureIgnoreCase (it only affects: Predicate.HaveName, Predicate.HaveNameStartingWith, Predicate.HaveNameEndingWith, Predicate.ResideInNamespace)\n\n- [SerachForDependencyInFieldConstant](documentation/api.md#optionsserachfordependencyinfieldconstant) - determines if dependency analysis should look for dependency in string field constant, default: false\n\n## Limitations\n\nNetArchTest is built on top of [jbevain/cecil](https://github.com/jbevain/cecil) thus it works on CIL level. Unfortunately, not every feature of C# language is represented in CIL, thus some things will never be available in NetArchTest, e.g.:\n- BenMorris/NetArchTest#81 - NetArchTest ignores a nameof expression \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnevespl%2Fnetarchtest.enhancededition","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnevespl%2Fnetarchtest.enhancededition","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnevespl%2Fnetarchtest.enhancededition/lists"}