Repository navigation
Most of this release fixes cases where ArchUnitNET missed dependencies, crashed while loading or evaluating, or drew a wrong diagram. Dependencies inside async methods are now found in Release builds and in async lambdas, so rules that only passed because those dependencies were missing may start failing. Please read Upgrading before you update. Also in this release: ResideInNamespaceOrBelow and NotBeNestedIn, and much better PlantUML export for slices.
Highlights
- Dependencies in async code are no longer lost. In optimized builds, every dependency inside an
asyncmethod body used to be missing, and so were the dependencies inside async lambdas. Negative rules such asNotDependOnAnycould therefore pass without checking anything. - Combined rules are now consistent.
a.And(b)andb.And(a)now give the same result, andRequirePositiveResultsis respected on both sides. - Slice diagrams are correct for more namespace layouts. Packages with the same name under different parents are no longer merged. Slice names that aren't valid PlantUML ids (like nested or generic type names) no longer produce broken diagrams, and C4 style works for slices without a namespace.
Added
-
ResideInNamespaceOrBelowand its negated forms. These match a namespace and all of its sub-namespaces, so a rule no longer needs to list every sub-namespace or use a regular expression like^Foo\.Bar(\.|$)."Foo.Bar"matchesFoo.BarandFoo.Bar.Baz, but notFoo.BarBaz, and the comparison ignores case likeResideInNamespacedoes. Available asResideInNamespaceOrBelow/NotResideInNamespaceOrBelowon conditions andResideInNamespaceOrBelow/DoNotResideInNamespaceOrBelowon predicates. #511 (@StefanGreve)Types().That().ResideInNamespaceOrBelow("MyProject.Model") .Should().NotBePublic()
-
NotBeNestedIn/AreNotNestedIn. These are the negated forms ofBeNestedIn/AreNestedIn, with the same overloads. #487 (@alexanderlinne)
Fixed
Loading and dependency analysis
- Async methods in Release builds. In optimized builds the compiler creates async state machines as structs, which the loader didn't recognize. It then analyzed only the small method that starts the state machine and lost every call,
newand field access inside the method body. The loader now reads the state machine fromAsyncStateMachineAttribute/IteratorStateMachineAttribute. #500 (@iamAdarshh), fixes #498 - Async lambdas. Dependencies inside async lambdas, e.g.
Register(async () => C3.Bar()), are now added to the method that contains the lambda. #505 (@mkay-sh), fixes #230 - Assemblies that can't be resolved.
ArchLoader.Build()threwAssemblyResolutionException(for example formscorlibin .NET Framework 4.8 SDK-style projects) when a referenced assembly couldn't be found. Types from such assemblies are now treated as unavailable, as intended. #502 (@mak638), fixes #497 - Types without a namespace or assembly. Namespace and assembly predicates and conditions threw a
NullReferenceExceptiononTypes(true)when the architecture contained function pointer types. Such types now simply don't reside in any namespace or assembly. #501 (@iamAdarshh), fixes #492
Rules
And/Orare commutative. A combined rule was built from the first rule's rule creator rather than from the rule itself. As a result, a rule whose predicate matched nothing counted as failing only when it was on the right-hand side ofAnd/Or.HasNoViolations()andEvaluate()now also agree when a predicate matches nothing, and a combined rule now uses the inner rule'sRequirePositiveResultsinstead of its own. #504 (@brandhuf)AreStatic/AreNotStaticon members whose static state is unknown now behave the same asBeStatic/NotBeStatic. #485 (@alexanderlinne)Interfaces().That()...As()/.Because()now returnGivenInterfacesConjunctionWithDescription, like the other rule types. #486 (@alexanderlinne)
Slices and PlantUML export
- Packages with the same name under different parents stay separate. PlantUML identifies a package by its name, so
Orders.DomainandBilling.Domainwere drawn as oneDomainpackage, and an arrow to one of them could end in the other. Each package is now declared with its full path as its alias (package "Domain" as App.Billing.Domain) and keeps its own name as its label. #499 (@alexanderlinne) - Nested slices are written as one package tree. Before, every shared parent package was reopened once per slice. The
.pumloutput is now shorter, indented by nesting level, and reads like a hand-written diagram. Hyperlinks on nested slices, which PlantUML used to reject, now work. #499 (@alexanderlinne) - Slice names that aren't valid PlantUML ids (for example
Outer+InnerorList`1, whichSlicedByproduces from type names) now get an id of their own. The name is still used as the label. #510 (@alexanderlinne) - C4 style works for slices without a namespace.
Slices().Matching(...)withGenerationOptions.C4Stylethrew aNullReferenceException. Patterns that start with a capture group, such asMatchingWithPackages("(**).Service"), crashed the exporter in the same way. #495, #509 (@alexanderlinne) OneToOneIfSameParentNamespacearrows now put component names in brackets like every other arrow type, so names that contain..or*no longer break the diagram. #508 (@alexanderlinne)- Slice equality depends only on the slice's identifier. Two slices with the same identifier used to compare as different when their type lists were separate instances. This affected the deduplication of slices in diagrams and of violations in frozen rules. #507 (@alexanderlinne)
Upgrading
No changes to the fluent API are required. The following fixes can still change the results of existing tests:
- Rules may find new violations. Dependencies inside
asyncmethods (in Release builds) and inside async lambdas are now part of the architecture. A rule that passed because they were missing will now report them. This most likely affects test suites that run in-c Release. Note that it is still recommended to run ArchUnitNET tests with-c Debugin order to have all features available. - Combined rules may fail where they passed before. If one side of an
And/Ormatches no objects, the combined rule now fails, unless that side usesWithoutRequiringPositiveResults(). Before, this depended on the order of the two rules. - The generated PlantUML text for slices has changed. It now uses package aliases, generated ids and indentation, and empty packages are drawn as package boxes. The rendered diagrams are otherwise unchanged. If you compare generated
.pumlfiles with saved copies, update the saved copies. - Removed
HaveAnyAttributesWithArguments(object, params object[])predicate overload. This leftover was missed when the otherparams object[]overloads were removed in 0.13.0. Pass a collection instead:HaveAnyAttributesWithArguments(new object[] { a, b }). #486 PropertyMemberPredicateDefinitionwas renamed toPropertyMemberPredicatesDefinitionto match the other definition classes. This only matters if you use the class directly instead of the fluent API. #489
Testing and CI
- Large expansion and cleanup of the fluent syntax and slice test suites. Snapshot output is now independent of loader order, and the assert helpers can no longer pass without checking anything. #484, #486, #488, #489, #490, #495 (@alexanderlinne)
- NuGet packages are now published with NuGet trusted publishing instead of an API key. #483 (@alexanderlinne)
- Development tools are pinned with mise, and hk runs formatting and lint checks (csharpier, actionlint, gitleaks, zizmor) through
mise run check. Also includes security fixes in the GitHub Actions workflows and removal of the old Travis configuration. #482 (@alexanderlinne) - TUnit tests were updated to TUnit 0.90.45 and now run on Microsoft.Testing.Platform, in parallel with the VSTest-based test projects (
mise run test). #491 (@mak638), #506 (@alexanderlinne)
New Contributors
- @iamAdarshh made their first contribution in #500
- @mkay-sh made their first contribution in #505
- @StefanGreve made their first contribution in #511
Full Changelog: 0.13.4...0.13.5