From 7b27d8265553f593c88b93fec079fb5fbbe45529 Mon Sep 17 00:00:00 2001 From: Ahmed Mohamed El Ahmar Date: Sat, 6 Sep 2025 23:10:58 +0100 Subject: [PATCH 1/2] feat: Add HavePrivateParameterlessConstructor and NotHavePrivateParameterlessConstructor conditions Add support for checking if classes have private parameterless constructors, which is essential for enforcing Domain-Driven Design patterns and ORM requirements. Signed-off-by: Ahmed Mohamed El Ahmar --- .../Classes/ClassConditionsDefinition.cs | 21 ++++++ .../Elements/Types/Classes/ClassesShould.cs | 12 +++ ArchUnitNETTests/ArchUnitNETTests.csproj | 2 +- .../ClassPrivateConstructorConditionTests.cs | 73 +++++++++++++++++++ .../Entities/PrivateConstructorTestClasses.cs | 56 ++++++++++++++ 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs create mode 100644 TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs index 5fd8b88ed..db1bc89fe 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs @@ -1,5 +1,6 @@ using System.Linq; using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; using ArchUnitNET.Fluent.Conditions; namespace ArchUnitNET.Fluent.Syntax.Elements.Types.Classes @@ -43,6 +44,16 @@ public static ICondition BeImmutable() "is not immutable" ); } + + public static ICondition HavePrivateParameterlessConstructor() + { + return new SimpleCondition( + cls => (cls.IsAbstract.HasValue && cls.IsAbstract.Value) || cls.GetConstructors().Any(c => + c.Visibility == Visibility.Private && !c.Parameters.Any()), + "have private parameterless constructor", + "does not have private parameterless constructor" + ); + } //Negations @@ -81,5 +92,15 @@ public static ICondition NotBeImmutable() "is immutable" ); } + + public static ICondition NotHavePrivateParameterlessConstructor() + { + return new SimpleCondition( + cls => (cls.IsAbstract.HasValue && cls.IsAbstract.Value) || !cls.GetConstructors().Any(c => + c.Visibility == Visibility.Private && !c.Parameters.Any()), + "not have private parameterless constructor", + "has private parameterless constructor" + ); + } } } diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs index 2788f9597..ef89d247f 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs @@ -33,6 +33,12 @@ public ClassesShouldConjunction BeImmutable() return new ClassesShouldConjunction(_ruleCreator); } + public ClassesShouldConjunction HavePrivateParameterlessConstructor() + { + _ruleCreator.AddCondition(ClassConditionsDefinition.HavePrivateParameterlessConstructor()); + return new ClassesShouldConjunction(_ruleCreator); + } + //Negations public ClassesShouldConjunction NotBeAbstract() @@ -58,5 +64,11 @@ public ClassesShouldConjunction NotBeImmutable() _ruleCreator.AddCondition(ClassConditionsDefinition.NotBeImmutable()); return new ClassesShouldConjunction(_ruleCreator); } + + public ClassesShouldConjunction NotHavePrivateParameterlessConstructor() + { + _ruleCreator.AddCondition(ClassConditionsDefinition.NotHavePrivateParameterlessConstructor()); + return new ClassesShouldConjunction(_ruleCreator); + } } } diff --git a/ArchUnitNETTests/ArchUnitNETTests.csproj b/ArchUnitNETTests/ArchUnitNETTests.csproj index 5d0b87223..b8be3da53 100644 --- a/ArchUnitNETTests/ArchUnitNETTests.csproj +++ b/ArchUnitNETTests/ArchUnitNETTests.csproj @@ -1,7 +1,7 @@ net9.0 - latest + default TNG Technology Consulting GmbH true true diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs b/ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs new file mode 100644 index 000000000..7c6986010 --- /dev/null +++ b/ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs @@ -0,0 +1,73 @@ +using ArchUnitNET.Domain; +using ArchUnitNET.Loader; +using TestAssembly.Domain.Entities; +using Xunit; +using static ArchUnitNET.Fluent.ArchRuleDefinition; + +namespace ArchUnitNETTests.Fluent.Syntax.Elements; + +public class ClassPrivateConstructorConditionTests +{ + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(ClassWithPrivateParameterlessConstructor).Assembly).Build(); + + [Fact] + public void HavePrivateParameterlessConstructor_ClassWithPrivateParameterlessConstructor_DoesNotViolate() + { + var rule = Classes() + .That().HaveName(nameof(ClassWithPrivateParameterlessConstructor)) + .Should().HavePrivateParameterlessConstructor(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void HavePrivateParameterlessConstructor_ClassWithoutPrivateParameterlessConstructor_Violates() + { + var rule = Classes() + .That().HaveName(nameof(ClassWithoutPrivateParameterlessConstructor)) + .Should().HavePrivateParameterlessConstructor(); + + Assert.False(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void NotHavePrivateParameterlessConstructor_ClassWithoutPrivateParameterlessConstructor_DoesNotViolate() + { + var rule = Classes() + .That().HaveName(nameof(ClassWithoutPrivateParameterlessConstructor)) + .Should().NotHavePrivateParameterlessConstructor(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void NotHavePrivateParameterlessConstructor_ClassWithPrivateParameterlessConstructor_Violates() + { + var rule = Classes() + .That().HaveName(nameof(ClassWithPrivateParameterlessConstructor)) + .Should().NotHavePrivateParameterlessConstructor(); + + Assert.False(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void HavePrivateParameterlessConstructor_AbstractClass_DoesNotViolate() + { + var rule = Classes() + .That().AreAbstract() + .Should().HavePrivateParameterlessConstructor(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void HavePrivateParameterlessConstructor_ClassWithOnlyParameterizedConstructors_Violates() + { + var rule = Classes() + .That().HaveName(nameof(ClassWithOnlyParameterizedConstructors)) + .Should().HavePrivateParameterlessConstructor(); + + Assert.False(rule.HasNoViolations(Architecture)); + } +} \ No newline at end of file diff --git a/TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs b/TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs new file mode 100644 index 000000000..983b2a440 --- /dev/null +++ b/TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs @@ -0,0 +1,56 @@ +namespace TestAssembly.Domain.Entities; + +public class ClassWithPrivateParameterlessConstructor +{ + private ClassWithPrivateParameterlessConstructor() + { + // Private parameterless constructor for ORM + } + + public ClassWithPrivateParameterlessConstructor(string name) + { + Name = name; + } + + public string Name { get; private set; } +} + +public class ClassWithoutPrivateParameterlessConstructor +{ + public ClassWithoutPrivateParameterlessConstructor() + { + // Public parameterless constructor + } + + public ClassWithoutPrivateParameterlessConstructor(string name) + { + Name = name; + } + + public string Name { get; private set; } +} + +public class ClassWithOnlyParameterizedConstructors +{ + public ClassWithOnlyParameterizedConstructors(string name) + { + Name = name; + } + + public ClassWithOnlyParameterizedConstructors(int id, string name) + { + Id = id; + Name = name; + } + + public int Id { get; private set; } + public string Name { get; private set; } +} + +public abstract class AbstractClassBase +{ + protected AbstractClassBase() + { + // Abstract classes should be excluded from checks + } +} \ No newline at end of file From 4cd515f7ecd5fcedb6b43c7b4146baf577fb8c51 Mon Sep 17 00:00:00 2001 From: Ahmed Mohamed El Ahmar Date: Tue, 16 Sep 2025 21:52:08 +0100 Subject: [PATCH 2/2] Add HaveAnyParameters and NotHaveAnyParameters conditions for MethodMembers - Add HaveAnyParameters() condition to check if methods have parameters - Add NotHaveAnyParameters() condition to check if methods have no parameters - Add corresponding methods to IMethodMemberConditions interface - Add comprehensive test coverage for both conditions - Test both regular methods and constructors - Verify failure descriptions in violation cases - Use correct IL method names (.ctor) for constructor tests - Remove redundant test cases that provided no additional value The new conditions enable architectural rules to enforce parameter requirements on methods and constructors, complementing existing method member conditions. Signed-off-by: Ahmed Mohamed El Ahmar --- .../MethodMembers/IMethodMemberConditions.cs | 2 + .../MethodMemberConditionsDefinition.cs | 26 +++ .../MethodMembers/MethodMembersShould.cs | 13 ++ .../Classes/ClassConditionsDefinition.cs | 21 --- .../Elements/Types/Classes/ClassesShould.cs | 12 -- .../ClassPrivateConstructorConditionTests.cs | 73 -------- .../Elements/MethodParameterConditionTests.cs | 159 ++++++++++++++++++ .../Entities/PrivateConstructorTestClasses.cs | 56 ------ .../Methods/MethodParameterTestClass.cs | 73 ++++++++ 9 files changed, 273 insertions(+), 162 deletions(-) delete mode 100644 ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs create mode 100644 ArchUnitNETTests/Fluent/Syntax/Elements/MethodParameterConditionTests.cs delete mode 100644 TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs create mode 100644 TestAssembly/Domain/Methods/MethodParameterTestClass.cs diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/IMethodMemberConditions.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/IMethodMemberConditions.cs index c95b97342..014626cbe 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/IMethodMemberConditions.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/IMethodMemberConditions.cs @@ -28,11 +28,13 @@ public interface IMethodMemberConditions TReturnType HaveReturnType(IObjectProvider types); TReturnType HaveReturnType(Type type, params Type[] moreTypes); TReturnType HaveReturnType(IEnumerable types); + TReturnType HaveAnyParameters(); //Negations TReturnType BeNoConstructor(); TReturnType NotBeVirtual(); + TReturnType NotHaveAnyParameters(); TReturnType NotBeCalledBy(IType firstType, params IType[] moreTypes); TReturnType NotBeCalledBy(Type type, params Type[] moreTypes); diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMemberConditionsDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMemberConditionsDefinition.cs index 096b92a7a..af70f98d6 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMemberConditionsDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMemberConditionsDefinition.cs @@ -1011,5 +1011,31 @@ bool Condition(MethodMember member) description ); } + + /// + /// Selects method members that have any parameters + /// + /// A condition that can be applied to method members + public static ICondition HaveAnyParameters() + { + return new SimpleCondition( + method => method.Parameters.Any(), + "have any parameters", + "does not have any parameters" + ); + } + + /// + /// Selects method members that do not have any parameters (parameterless) + /// + /// A condition that can be applied to method members + public static ICondition NotHaveAnyParameters() + { + return new SimpleCondition( + method => !method.Parameters.Any(), + "not have any parameters", + "has parameters" + ); + } } } diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMembersShould.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMembersShould.cs index 30e0b3ef0..aad6b71be 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMembersShould.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/MethodMembers/MethodMembersShould.cs @@ -297,5 +297,18 @@ public MethodMembersShouldConjunction NotHaveReturnType(IEnumerable types) _ruleCreator.AddCondition(MethodMemberConditionsDefinition.NotHaveReturnType(types)); return new MethodMembersShouldConjunction(_ruleCreator); } + + + public MethodMembersShouldConjunction HaveAnyParameters() + { + _ruleCreator.AddCondition(MethodMemberConditionsDefinition.HaveAnyParameters()); + return new MethodMembersShouldConjunction(_ruleCreator); + } + + public MethodMembersShouldConjunction NotHaveAnyParameters() + { + _ruleCreator.AddCondition(MethodMemberConditionsDefinition.NotHaveAnyParameters()); + return new MethodMembersShouldConjunction(_ruleCreator); + } } } diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs index db1bc89fe..5fd8b88ed 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassConditionsDefinition.cs @@ -1,6 +1,5 @@ using System.Linq; using ArchUnitNET.Domain; -using ArchUnitNET.Domain.Extensions; using ArchUnitNET.Fluent.Conditions; namespace ArchUnitNET.Fluent.Syntax.Elements.Types.Classes @@ -44,16 +43,6 @@ public static ICondition BeImmutable() "is not immutable" ); } - - public static ICondition HavePrivateParameterlessConstructor() - { - return new SimpleCondition( - cls => (cls.IsAbstract.HasValue && cls.IsAbstract.Value) || cls.GetConstructors().Any(c => - c.Visibility == Visibility.Private && !c.Parameters.Any()), - "have private parameterless constructor", - "does not have private parameterless constructor" - ); - } //Negations @@ -92,15 +81,5 @@ public static ICondition NotBeImmutable() "is immutable" ); } - - public static ICondition NotHavePrivateParameterlessConstructor() - { - return new SimpleCondition( - cls => (cls.IsAbstract.HasValue && cls.IsAbstract.Value) || !cls.GetConstructors().Any(c => - c.Visibility == Visibility.Private && !c.Parameters.Any()), - "not have private parameterless constructor", - "has private parameterless constructor" - ); - } } } diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs index ef89d247f..2788f9597 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Types/Classes/ClassesShould.cs @@ -33,12 +33,6 @@ public ClassesShouldConjunction BeImmutable() return new ClassesShouldConjunction(_ruleCreator); } - public ClassesShouldConjunction HavePrivateParameterlessConstructor() - { - _ruleCreator.AddCondition(ClassConditionsDefinition.HavePrivateParameterlessConstructor()); - return new ClassesShouldConjunction(_ruleCreator); - } - //Negations public ClassesShouldConjunction NotBeAbstract() @@ -64,11 +58,5 @@ public ClassesShouldConjunction NotBeImmutable() _ruleCreator.AddCondition(ClassConditionsDefinition.NotBeImmutable()); return new ClassesShouldConjunction(_ruleCreator); } - - public ClassesShouldConjunction NotHavePrivateParameterlessConstructor() - { - _ruleCreator.AddCondition(ClassConditionsDefinition.NotHavePrivateParameterlessConstructor()); - return new ClassesShouldConjunction(_ruleCreator); - } } } diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs b/ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs deleted file mode 100644 index 7c6986010..000000000 --- a/ArchUnitNETTests/Fluent/Syntax/Elements/ClassPrivateConstructorConditionTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -using ArchUnitNET.Domain; -using ArchUnitNET.Loader; -using TestAssembly.Domain.Entities; -using Xunit; -using static ArchUnitNET.Fluent.ArchRuleDefinition; - -namespace ArchUnitNETTests.Fluent.Syntax.Elements; - -public class ClassPrivateConstructorConditionTests -{ - private static readonly Architecture Architecture = - new ArchLoader().LoadAssembly(typeof(ClassWithPrivateParameterlessConstructor).Assembly).Build(); - - [Fact] - public void HavePrivateParameterlessConstructor_ClassWithPrivateParameterlessConstructor_DoesNotViolate() - { - var rule = Classes() - .That().HaveName(nameof(ClassWithPrivateParameterlessConstructor)) - .Should().HavePrivateParameterlessConstructor(); - - Assert.True(rule.HasNoViolations(Architecture)); - } - - [Fact] - public void HavePrivateParameterlessConstructor_ClassWithoutPrivateParameterlessConstructor_Violates() - { - var rule = Classes() - .That().HaveName(nameof(ClassWithoutPrivateParameterlessConstructor)) - .Should().HavePrivateParameterlessConstructor(); - - Assert.False(rule.HasNoViolations(Architecture)); - } - - [Fact] - public void NotHavePrivateParameterlessConstructor_ClassWithoutPrivateParameterlessConstructor_DoesNotViolate() - { - var rule = Classes() - .That().HaveName(nameof(ClassWithoutPrivateParameterlessConstructor)) - .Should().NotHavePrivateParameterlessConstructor(); - - Assert.True(rule.HasNoViolations(Architecture)); - } - - [Fact] - public void NotHavePrivateParameterlessConstructor_ClassWithPrivateParameterlessConstructor_Violates() - { - var rule = Classes() - .That().HaveName(nameof(ClassWithPrivateParameterlessConstructor)) - .Should().NotHavePrivateParameterlessConstructor(); - - Assert.False(rule.HasNoViolations(Architecture)); - } - - [Fact] - public void HavePrivateParameterlessConstructor_AbstractClass_DoesNotViolate() - { - var rule = Classes() - .That().AreAbstract() - .Should().HavePrivateParameterlessConstructor(); - - Assert.True(rule.HasNoViolations(Architecture)); - } - - [Fact] - public void HavePrivateParameterlessConstructor_ClassWithOnlyParameterizedConstructors_Violates() - { - var rule = Classes() - .That().HaveName(nameof(ClassWithOnlyParameterizedConstructors)) - .Should().HavePrivateParameterlessConstructor(); - - Assert.False(rule.HasNoViolations(Architecture)); - } -} \ No newline at end of file diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/MethodParameterConditionTests.cs b/ArchUnitNETTests/Fluent/Syntax/Elements/MethodParameterConditionTests.cs new file mode 100644 index 000000000..29ef3b4e7 --- /dev/null +++ b/ArchUnitNETTests/Fluent/Syntax/Elements/MethodParameterConditionTests.cs @@ -0,0 +1,159 @@ +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Loader; +using TestAssembly.Domain.Methods; +using Xunit; +using static ArchUnitNET.Fluent.ArchRuleDefinition; + +namespace ArchUnitNETTests.Fluent.Syntax.Elements; + +public class MethodParameterConditionTests +{ + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(ClassWithPrivateParameterlessConstructor).Assembly).Build(); + + [Fact] + public void HaveAnyParameters_MethodWithParameters_DoesNotViolate() + { + var rule = MethodMembers() + .That() + .AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithMethods))) + .And() + .HaveName("MethodWithParameters(System.String,System.Int32)") + .Should() + .HaveAnyParameters(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void HaveAnyParameters_MethodWithoutParameters_Violates() + { + var rule = MethodMembers() + .That() + .AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithMethods))) + .And() + .HaveName("MethodWithoutParameters()") + .Should() + .HaveAnyParameters(); + + Assert.False(rule.HasNoViolations(Architecture)); + + var evaluation = rule.Evaluate(Architecture); + var violations = evaluation.ToList(); + Assert.Single(violations); + Assert.Contains("does not have any parameters", violations.First().Description); + } + + [Fact] + public void NotHaveAnyParameters_MethodWithoutParameters_DoesNotViolate() + { + var rule = MethodMembers() + .That() + .AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithMethods))) + .And().HaveName("PrivateMethodWithoutParameters()") + .Should().NotHaveAnyParameters(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void NotHaveAnyParameters_MethodWithParameters_Violates() + { + var rule = MethodMembers() + .That().HaveName("MethodWithParameters(System.String,System.Int32)") + .Should().NotHaveAnyParameters(); + + Assert.False(rule.HasNoViolations(Architecture)); + + var evaluation = rule.Evaluate(Architecture); + var violations = evaluation.ToList(); + Assert.Single(violations); + Assert.Contains("has parameters", violations.First().Description); + } + + [Fact] + public void HaveAnyParameters_ConstructorWithParameters_DoesNotViolate() + { + var rule = MethodMembers() + .That().AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithOnlyParameterizedConstructors))) + .And().AreConstructors() + .Should().HaveAnyParameters(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void HaveAnyParameters_ParameterlessConstructor_Violates() + { + var rule = MethodMembers() + .That().AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithPublicParameterlessConstructor))) + .And().AreConstructors() + .And().HaveName(".ctor()") + .Should().HaveAnyParameters(); + + Assert.False(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void PrivateConstructorWithoutParameters_CompositeRule_DoesNotViolate() + { + var rule = MethodMembers() + .That().AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithPrivateParameterlessConstructor))) + .And().AreConstructors() + .And().ArePrivate() + .Should().NotHaveAnyParameters(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void PublicConstructorWithParameters_CompositeRule_DoesNotViolate() + { + var rule = MethodMembers() + .That().AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithPrivateParameterlessConstructor))) + .And().AreConstructors() + .And().ArePublic() + .Should().HaveAnyParameters(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void PrivateMethodWithoutParameters_DoesNotViolate() + { + var rule = MethodMembers() + .That() + .AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithMethods))) + .And() + .HaveName("PrivateMethodWithoutParameters()") + .And() + .ArePrivate() + .Should() + .NotHaveAnyParameters(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void SpecificClass_AllConstructorsHaveParameters_DoesNotViolate() + { + var rule = MethodMembers() + .That().AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithOnlyParameterizedConstructors))) + .And().AreConstructors() + .Should().HaveAnyParameters(); + + Assert.True(rule.HasNoViolations(Architecture)); + } + + [Fact] + public void SpecificClass_HasParameterlessConstructor_Violates() + { + var rule = MethodMembers() + .That().AreDeclaredIn(Classes().That().HaveName(nameof(ClassWithPublicParameterlessConstructor))) + .And().AreConstructors() + .Should().HaveAnyParameters(); + + Assert.False(rule.HasNoViolations(Architecture)); + } +} diff --git a/TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs b/TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs deleted file mode 100644 index 983b2a440..000000000 --- a/TestAssembly/Domain/Entities/PrivateConstructorTestClasses.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace TestAssembly.Domain.Entities; - -public class ClassWithPrivateParameterlessConstructor -{ - private ClassWithPrivateParameterlessConstructor() - { - // Private parameterless constructor for ORM - } - - public ClassWithPrivateParameterlessConstructor(string name) - { - Name = name; - } - - public string Name { get; private set; } -} - -public class ClassWithoutPrivateParameterlessConstructor -{ - public ClassWithoutPrivateParameterlessConstructor() - { - // Public parameterless constructor - } - - public ClassWithoutPrivateParameterlessConstructor(string name) - { - Name = name; - } - - public string Name { get; private set; } -} - -public class ClassWithOnlyParameterizedConstructors -{ - public ClassWithOnlyParameterizedConstructors(string name) - { - Name = name; - } - - public ClassWithOnlyParameterizedConstructors(int id, string name) - { - Id = id; - Name = name; - } - - public int Id { get; private set; } - public string Name { get; private set; } -} - -public abstract class AbstractClassBase -{ - protected AbstractClassBase() - { - // Abstract classes should be excluded from checks - } -} \ No newline at end of file diff --git a/TestAssembly/Domain/Methods/MethodParameterTestClass.cs b/TestAssembly/Domain/Methods/MethodParameterTestClass.cs new file mode 100644 index 000000000..cb00091b4 --- /dev/null +++ b/TestAssembly/Domain/Methods/MethodParameterTestClass.cs @@ -0,0 +1,73 @@ +namespace TestAssembly.Domain.Methods; + +public class ClassWithPrivateParameterlessConstructor +{ + // Private parameterless constructor + private ClassWithPrivateParameterlessConstructor() + { + } + + // Public constructor with parameters + public ClassWithPrivateParameterlessConstructor(string value) + { + Value = value; + } + + public string Value { get; set; } +} + +public class ClassWithPublicParameterlessConstructor +{ + // Public parameterless constructor + public ClassWithPublicParameterlessConstructor() + { + } + + // Public constructor with parameters + public ClassWithPublicParameterlessConstructor(int number) + { + Number = number; + } + + public int Number { get; set; } +} + +public class ClassWithOnlyParameterizedConstructors +{ + // Only constructors with parameters + public ClassWithOnlyParameterizedConstructors(string name) + { + Name = name; + } + + public ClassWithOnlyParameterizedConstructors(string name, int id) + { + Name = name; + Id = id; + } + + public string Name { get; set; } + public int Id { get; set; } +} + +public class ClassWithMethods +{ + public ClassWithMethods() + { + } + + // Method without parameters + public void MethodWithoutParameters() + { + } + + // Method with parameters + public void MethodWithParameters(string input, int count) + { + } + + // Private method without parameters + private void PrivateMethodWithoutParameters() + { + } +} \ No newline at end of file