diff --git a/Src/FluentAssertions/Common/ObjectExtensions.cs b/Src/FluentAssertions/Common/ObjectExtensions.cs index 7ba5e3bdc..e1bf0f003 100644 --- a/Src/FluentAssertions/Common/ObjectExtensions.cs +++ b/Src/FluentAssertions/Common/ObjectExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using FluentAssertions.Formatting; namespace FluentAssertions.Common; @@ -77,4 +78,12 @@ ushort or uint or ulong); } + + /// + /// Convenience method to format an object to a string using the class. + /// + public static string ToFormattedString(this object source) + { + return Formatter.ToString(source); + } } diff --git a/Src/FluentAssertions/Execution/AssertionChain.cs b/Src/FluentAssertions/Execution/AssertionChain.cs index ef72a3f6b..ce085dcfe 100644 --- a/Src/FluentAssertions/Execution/AssertionChain.cs +++ b/Src/FluentAssertions/Execution/AssertionChain.cs @@ -253,12 +253,23 @@ private Continuation FailWith(Func getFailureReason) public void OverrideCallerIdentifier(Func getCallerIdentifier) { this.getCallerIdentifier = getCallerIdentifier; + HasOverriddenCallerIdentifier = true; } public AssertionChain WithCallerPostfix(string postfix) { var originalCallerIdentifier = getCallerIdentifier; getCallerIdentifier = () => originalCallerIdentifier() + postfix; + HasOverriddenCallerIdentifier = true; + + return this; + } + + public AssertionChain WithCallerPrefix(string prefix) + { + var originalCallerIdentifier = getCallerIdentifier; + getCallerIdentifier = () => prefix + originalCallerIdentifier(); + HasOverriddenCallerIdentifier = true; return this; } @@ -305,4 +316,6 @@ public AssertionChain UsingLineBreaks return this; } } + + public bool HasOverriddenCallerIdentifier { get; private set; } } diff --git a/Src/FluentAssertions/Formatting/Formatter.cs b/Src/FluentAssertions/Formatting/Formatter.cs index 93ba19030..4ac3f13bf 100644 --- a/Src/FluentAssertions/Formatting/Formatter.cs +++ b/Src/FluentAssertions/Formatting/Formatter.cs @@ -26,6 +26,7 @@ public static class Formatter new XElementValueFormatter(), new XAttributeValueFormatter(), new PropertyInfoFormatter(), + new MethodInfoFormatter(), new NullValueFormatter(), new GuidValueFormatter(), new DateTimeOffsetValueFormatter(), diff --git a/Src/FluentAssertions/Formatting/MethodInfoFormatter.cs b/Src/FluentAssertions/Formatting/MethodInfoFormatter.cs new file mode 100644 index 000000000..ad32c3199 --- /dev/null +++ b/Src/FluentAssertions/Formatting/MethodInfoFormatter.cs @@ -0,0 +1,31 @@ +using System.Reflection; + +namespace FluentAssertions.Formatting; + +public class MethodInfoFormatter : IValueFormatter +{ + /// + /// Indicates whether the current can handle the specified . + /// + /// The value for which to create a . + /// + /// if the current can handle the specified value; otherwise, . + /// + public bool CanHandle(object value) + { + return value is MethodInfo; + } + + public void Format(object value, FormattedObjectGraph formattedGraph, FormattingContext context, FormatChild formatChild) + { + var method = (MethodInfo)value; + if (method is null) + { + formattedGraph.AddFragment(""); + } + else + { + formattedGraph.AddFragment($"{method!.DeclaringType!.Name + "." + method.Name}"); + } + } +} diff --git a/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs b/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs index c9746c290..7ec15ed75 100644 --- a/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs +++ b/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs @@ -18,6 +18,17 @@ public bool CanHandle(object value) public void Format(object value, FormattedObjectGraph formattedGraph, FormattingContext context, FormatChild formatChild) { - formattedGraph.AddFragment(((PropertyInfo)value).Name); + var property = (PropertyInfo)value; + + if (property is null) + { + formattedGraph.AddFragment(""); + } + else + { + var propTypeName = property.PropertyType.Name; + + formattedGraph.AddFragment($"{propTypeName} {property.DeclaringType}.{property.Name}"); + } } } diff --git a/Src/FluentAssertions/Types/MethodBaseAssertions.cs b/Src/FluentAssertions/Types/MethodBaseAssertions.cs index 7aaac75eb..3b7c995b5 100644 --- a/Src/FluentAssertions/Types/MethodBaseAssertions.cs +++ b/Src/FluentAssertions/Types/MethodBaseAssertions.cs @@ -47,17 +47,20 @@ public AndConstraint HaveAccessModifier( assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith($"Expected method to be {accessModifier}{{reason}}, but {{context:member}} is ."); + .FailWith($"Expected method to be {accessModifier}{{reason}}, but {{context:method}} is ."); if (assertionChain.Succeeded) { CSharpAccessModifier subjectAccessModifier = Subject.GetCSharpAccessModifier(); + var subject = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "method " + Subject.ToFormattedString(); + assertionChain .ForCondition(accessModifier == subjectAccessModifier) .BecauseOf(because, becauseArgs) - .FailWith( - $"Expected method {Subject!.Name} to be {accessModifier}{{reason}}, but it is {subjectAccessModifier}."); + .FailWith($"Expected {subject} to be {accessModifier}{{reason}}, but it is {subjectAccessModifier}."); } return new AndConstraint((TAssertions)this); @@ -90,10 +93,14 @@ public AndConstraint NotHaveAccessModifier(CSharpAccessModifier acc { CSharpAccessModifier subjectAccessModifier = Subject.GetCSharpAccessModifier(); + var subject = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "method " + Subject.ToFormattedString(); + assertionChain .ForCondition(accessModifier != subjectAccessModifier) .BecauseOf(because, becauseArgs) - .FailWith($"Expected method {Subject!.Name} not to be {accessModifier}{{reason}}, but it is."); + .FailWith($"Expected {subject} not to be {accessModifier}{{reason}}, but it is."); } return new AndConstraint((TAssertions)this); diff --git a/Src/FluentAssertions/Types/PropertyInfoAssertions.cs b/Src/FluentAssertions/Types/PropertyInfoAssertions.cs index a1beee41d..11d3b9b80 100644 --- a/Src/FluentAssertions/Types/PropertyInfoAssertions.cs +++ b/Src/FluentAssertions/Types/PropertyInfoAssertions.cs @@ -4,6 +4,7 @@ using System.Reflection; using FluentAssertions.Common; using FluentAssertions.Execution; +using FluentAssertions.Formatting; namespace FluentAssertions.Types; @@ -44,7 +45,7 @@ public AndConstraint BeVirtual( assertionChain .ForCondition(Subject.IsVirtual()) .BecauseOf(because, becauseArgs) - .FailWith($"Expected property {GetDescriptionFor(Subject)} to be virtual{{reason}}, but it is not."); + .FailWith("Expected property {0} to be virtual{{reason}}, but it is not.", Subject); } return new AndConstraint(this); @@ -60,7 +61,8 @@ public AndConstraint BeVirtual( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeVirtual([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeVirtual([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { assertionChain .BecauseOf(because, becauseArgs) @@ -72,7 +74,7 @@ public AndConstraint NotBeVirtual([StringSyntax("Composi assertionChain .ForCondition(!Subject.IsVirtual()) .BecauseOf(because, becauseArgs) - .FailWith($"Expected property {GetDescriptionFor(Subject)} not to be virtual{{reason}}, but it is."); + .FailWith("Expected property {0} not to be virtual{{reason}}, but it is.", Subject); } return new AndConstraint(this); @@ -130,21 +132,17 @@ public AndConstraint BeWritable(CSharpAccessModifier acc assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith($"Expected {Identifier} to be {accessModifier}{{reason}}, but {{context:property}} is ."); + .FailWith($"Expected {{context:property}} to be {accessModifier}{{reason}}, but it is .") + .Then + .ForCondition(Subject!.CanWrite) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {0} to have a setter {{reason}}.", assertionChain.HasOverriddenCallerIdentifier ? assertionChain.CallerIdentifier : Subject); if (assertionChain.Succeeded) { - assertionChain - .ForCondition(Subject!.CanWrite) - .BecauseOf(because, becauseArgs) - .FailWith( - "Expected {context:property} {0} to have a setter{reason}.", - Subject); - - if (assertionChain.Succeeded) - { - Subject!.GetSetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); - } + assertionChain.WithCallerPrefix("setter of "); + assertionChain.ReuseOnce(); + Subject!.GetSetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); } return new AndConstraint(this); @@ -166,17 +164,11 @@ public AndConstraint NotBeWritable( assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith("Expected property not to have a setter{reason}, but {context:property} is ."); - - if (assertionChain.Succeeded) - { - assertionChain - .ForCondition(!Subject!.CanWrite) - .BecauseOf(because, becauseArgs) - .FailWith( - "Expected {context:property} {0} not to have a setter{reason}.", - Subject); - } + .FailWith("Expected {context:property} not to have a setter{reason}, but it is .") + .Then + .ForCondition(!Subject!.CanWrite) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {0} not to have a setter {{reason}}.", Subject); return new AndConstraint(this); } @@ -191,7 +183,8 @@ public AndConstraint NotBeWritable( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint BeReadable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeReadable([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { assertionChain .BecauseOf(because, becauseArgs) @@ -202,7 +195,7 @@ public AndConstraint BeReadable([StringSyntax("Composite { assertionChain.ForCondition(Subject!.CanRead) .BecauseOf(because, becauseArgs) - .FailWith("Expected property " + Subject.Name + " to have a getter{reason}, but it does not."); + .FailWith("Expected property {0}, to have a getter{reason}, but it does not.", Subject); } return new AndConstraint(this); @@ -229,18 +222,15 @@ public AndConstraint BeReadable(CSharpAccessModifier acc assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith($"Expected {Identifier} to be {accessModifier}{{reason}}, but {{context:property}} is ."); + .FailWith($"Expected {{context:property}} to be {accessModifier}{{reason}}, but it is .") + .Then + .ForCondition(Subject!.CanRead) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {0} to have a getter {{reason}}, but it does not.", Subject); if (assertionChain.Succeeded) { - assertionChain.ForCondition(Subject!.CanRead) - .BecauseOf(because, becauseArgs) - .FailWith("Expected property " + Subject.Name + " to have a getter{reason}, but it does not."); - - if (assertionChain.Succeeded) - { - Subject!.GetGetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); - } + Subject!.GetGetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); } return new AndConstraint(this); @@ -270,8 +260,7 @@ public AndConstraint NotBeReadable( .ForCondition(!Subject!.CanRead) .BecauseOf(because, becauseArgs) .FailWith( - "Expected {context:property} {0} not to have a getter{reason}.", - Subject); + "Expected {0} not to have a getter {{reason}}.", Subject); } return new AndConstraint(this); @@ -303,8 +292,8 @@ public AndConstraint Return(Type propertyType, { assertionChain.ForCondition(Subject!.PropertyType == propertyType) .BecauseOf(because, becauseArgs) - .FailWith("Expected Type of property " + Subject.Name + " to be {0}{reason}, but it is {1}.", - propertyType, Subject.PropertyType); + .FailWith("Expected type of property {2} to be {0}{reason}, but it is {1}.", + propertyType, Subject.PropertyType, Subject); } return new AndConstraint(this); @@ -321,7 +310,8 @@ public AndConstraint Return(Type propertyType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Return([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Return([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return Return(typeof(TReturn), because, becauseArgs); } @@ -353,7 +343,7 @@ public AndConstraint NotReturn(Type propertyType, assertionChain .ForCondition(Subject!.PropertyType != propertyType) .BecauseOf(because, becauseArgs) - .FailWith("Expected Type of property " + Subject.Name + " not to be {0}{reason}, but it is.", propertyType); + .FailWith("Expected Type of property {1} not to be {0}{reason}, but it is.", propertyType, Subject); } return new AndConstraint(this); @@ -370,24 +360,13 @@ public AndConstraint NotReturn(Type propertyType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotReturn([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotReturn([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotReturn(typeof(TReturn), because, becauseArgs); } - internal static string GetDescriptionFor(PropertyInfo property) - { - if (property is null) - { - return string.Empty; - } - - var propTypeName = property.PropertyType.Name; - - return $"{propTypeName} {property.DeclaringType}.{property.Name}"; - } - - internal override string SubjectDescription => GetDescriptionFor(Subject); + internal override string SubjectDescription => Formatter.ToString(Subject); /// /// Returns the type of the subject the assertion applies on. diff --git a/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs b/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs index a4aa31bb5..4aca17468 100644 --- a/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs +++ b/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs @@ -6,6 +6,7 @@ using System.Reflection; using FluentAssertions.Common; using FluentAssertions.Execution; +using FluentAssertions.Formatting; namespace FluentAssertions.Types; @@ -221,7 +222,7 @@ private PropertyInfo[] GetPropertiesWith() private static string GetDescriptionsFor(IEnumerable properties) { - IEnumerable descriptions = properties.Select(property => PropertyInfoAssertions.GetDescriptionFor(property)); + IEnumerable descriptions = properties.Select(property => Formatter.ToString(property)); return string.Join(Environment.NewLine, descriptions); } diff --git a/Src/FluentAssertions/Types/TypeAssertions.cs b/Src/FluentAssertions/Types/TypeAssertions.cs index 0af347192..55cf26d97 100644 --- a/Src/FluentAssertions/Types/TypeAssertions.cs +++ b/Src/FluentAssertions/Types/TypeAssertions.cs @@ -269,7 +269,7 @@ public AndWhich BeDecoratedWith( /// Zero or more objects to format using the placeholders in . /// /// is . - public AndWhichConstraint BeDecoratedWith( + public AndWhich BeDecoratedWith( Expression> isMatchingAttributePredicate, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TAttribute : Attribute @@ -287,7 +287,7 @@ public AndWhichConstraint BeDecoratedWith(this, attributes); + return new AndWhich(this, attributes); } /// @@ -300,7 +300,7 @@ public AndWhichConstraint BeDecoratedWith /// Zero or more objects to format using the placeholders in . /// - public AndWhichConstraint BeDecoratedWithOrInherit( + public AndWhich BeDecoratedWithOrInherit( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TAttribute : Attribute { @@ -312,7 +312,7 @@ public AndWhichConstraint BeDecoratedWithOrInherit(this, attributes); + return new AndWhich(this, attributes); } /// @@ -330,7 +330,7 @@ public AndWhichConstraint BeDecoratedWithOrInherit. /// /// is . - public AndWhichConstraint BeDecoratedWithOrInherit( + public AndWhich BeDecoratedWithOrInherit( Expression> isMatchingAttributePredicate, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TAttribute : Attribute @@ -348,7 +348,7 @@ public AndWhichConstraint BeDecoratedWithOrInherit(this, attributes); + return new AndWhich(this, attributes); } /// @@ -889,7 +889,7 @@ public AndConstraint NotBeStatic([StringSyntax("CompositeFormat" /// is . /// is . /// is empty. - public AndWhichConstraint HaveProperty( + public AndWhich HaveProperty( Type propertyType, string name, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { @@ -907,7 +907,6 @@ public AndWhichConstraint HaveProperty( if (assertionChain.Succeeded) { propertyInfo = Subject.FindPropertyByName(name); - var propertyInfoDescription = PropertyInfoAssertions.GetDescriptionFor(propertyInfo); assertionChain .BecauseOf(because, becauseArgs) @@ -915,10 +914,10 @@ public AndWhichConstraint HaveProperty( .FailWith($"Expected {propertyType.Name} {Subject}.{name} to exist{{reason}}, but it does not.") .Then .ForCondition(propertyInfo.PropertyType == propertyType) - .FailWith($"Expected {propertyInfoDescription} to be of type {propertyType}{{reason}}, but it is not."); + .FailWith($"Expected {0} to be of type {propertyType}{{reason}}, but it is not.", propertyInfo); } - return new AndWhichConstraint(this, propertyInfo); + return new AndWhich(this, propertyInfo); } /// @@ -936,7 +935,7 @@ public AndWhichConstraint HaveProperty( /// /// is . /// is empty. - public AndWhichConstraint HaveProperty( + public AndWhich HaveProperty( string name, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { return HaveProperty(typeof(TProperty), name, because, becauseArgs); @@ -968,12 +967,11 @@ public AndConstraint NotHaveProperty(string name, if (assertionChain.Succeeded) { PropertyInfo propertyInfo = Subject.FindPropertyByName(name); - var propertyInfoDescription = PropertyInfoAssertions.GetDescriptionFor(propertyInfo); assertionChain .BecauseOf(because, becauseArgs) .ForCondition(propertyInfo is null) - .FailWith($"Expected {propertyInfoDescription} to not exist{{reason}}, but it does."); + .FailWith("Expected {0} to not exist{{reason}}, but it does.", propertyInfo); } return new AndConstraint(this); @@ -1275,18 +1273,20 @@ public AndConstraint NotHaveExplicitMethod( /// /// is . /// is . - public AndWhichConstraint HaveIndexer( + public AndWhich HaveIndexer( Type indexerType, IEnumerable parameterTypes, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(indexerType); Guard.ThrowIfArgumentIsNull(parameterTypes); + string parameterString = GetParameterString(parameterTypes); + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( - $"Expected {indexerType.Name} {{context:type}}[{GetParameterString(parameterTypes)}] to exist{{reason}}" + + $"Expected {indexerType.Name} {{context:type}}[{parameterString}] to exist{{reason}}" + ", but {context:type} is ."); PropertyInfo propertyInfo = null; @@ -1294,20 +1294,20 @@ public AndWhichConstraint HaveIndexer( if (assertionChain.Succeeded) { propertyInfo = Subject.GetIndexerByParameterTypes(parameterTypes); - var propertyInfoDescription = PropertyInfoAssertions.GetDescriptionFor(propertyInfo); assertionChain .BecauseOf(because, becauseArgs) .ForCondition(propertyInfo is not null) .FailWith( - $"Expected {indexerType.Name} {Subject}[{GetParameterString(parameterTypes)}] to exist{{reason}}" + + $"Expected {indexerType.Name} {Subject}[{parameterString}] to exist{{reason}}" + ", but it does not.") .Then .ForCondition(propertyInfo.PropertyType == indexerType) - .FailWith($"Expected {propertyInfoDescription} to be of type {indexerType}{{reason}}, but it is not."); + .FailWith("Expected {0} to be of type {1}{reason}, but it is not.", propertyInfo, indexerType); } - return new AndWhichConstraint(this, propertyInfo); + return new AndWhich(this, propertyInfo, assertionChain, + $"[{parameterString}]"); } /// diff --git a/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs index 0bd236d7b..debf5e2c0 100644 --- a/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs @@ -191,7 +191,8 @@ public void Can_continue_assertions_on_the_found_type() .Which.Should().BeDecoratedWith(); // Assert - act.Should().Throw().WithMessage("blah"); + act.Should().Throw() + .WithMessage("Expected*WellKnownClassWithAttribute*decorated*SerializableAttribute*not found."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs index 84a2bf8dc..997f09fb2 100644 --- a/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs @@ -389,7 +389,7 @@ public void When_asserting_a_private_member_is_protected_it_throws_with_a_useful // Assert act.Should().Throw() .WithMessage( - "Expected method PrivateMethod to be Protected because we want to test the error message, but it is " + + "Expected method TestClass.PrivateMethod to be Protected because we want to test the error message, but it is " + "Private."); } @@ -426,7 +426,7 @@ public void When_asserting_a_protected_member_is_public_it_throws_with_a_useful_ // Assert act.Should().Throw() .WithMessage( - "Expected method set_ProtectedSetProperty to be Public because we want to test the error message, but it" + + "Expected method TestClass.set_ProtectedSetProperty to be Public because we want to test the error message, but it" + " is Protected."); } @@ -463,7 +463,7 @@ public void When_asserting_a_public_member_is_internal_it_throws_with_a_useful_m // Assert act.Should().Throw() .WithMessage( - "Expected method get_PublicGetProperty to be Internal because we want to test the error message, but it" + + "Expected method TestClass.get_PublicGetProperty to be Internal because we want to test the error message, but it" + " is Public."); } @@ -495,7 +495,7 @@ public void When_asserting_an_internal_member_is_protectedInternal_it_throws_wit // Assert act.Should().Throw() .WithMessage( - "Expected method InternalMethod to be ProtectedInternal because we want to test the error message, but" + + "Expected method TestClass.InternalMethod to be ProtectedInternal because we want to test the error message, but" + " it is Internal."); } @@ -526,7 +526,7 @@ public void When_asserting_a_protected_internal_member_is_private_it_throws_with // Assert act.Should().Throw() .WithMessage( - "Expected method ProtectedInternalMethod to be Private because we want to test the error message, but it is " + + "Expected method TestClass.ProtectedInternalMethod to be Private because we want to test the error message, but it is " + "ProtectedInternal."); } @@ -604,7 +604,7 @@ public void When_asserting_a_private_member_is_not_private_it_throws_with_a_usef // Assert act.Should().Throw() - .WithMessage("Expected method PrivateMethod not to be Private*because we want to test the error message*"); + .WithMessage("Expected method TestClass.PrivateMethod not to be Private*because we want to test the error message*"); } [Fact] @@ -638,7 +638,7 @@ public void When_asserting_a_protected_member_is_not_protected_it_throws_with_a_ // Assert act.Should().Throw() .WithMessage( - "Expected method set_ProtectedSetProperty not to be Protected*because we want to test the error message*"); + "Expected method TestClass.set_ProtectedSetProperty not to be Protected*because we want to test the error message*"); } [Fact] @@ -686,7 +686,7 @@ public void When_asserting_a_public_member_is_not_public_it_throws_with_a_useful // Assert act.Should().Throw() - .WithMessage("Expected method get_PublicGetProperty not to be Public*because we want to test the error message*"); + .WithMessage("Expected method TestClass.get_PublicGetProperty not to be Public*because we want to test the error message*"); } [Fact] @@ -716,7 +716,7 @@ public void When_asserting_an_internal_member_is_not_internal_it_throws_with_a_u // Assert act.Should().Throw() - .WithMessage("Expected method InternalMethod not to be Internal*because we want to test the error message*"); + .WithMessage("Expected method TestClass.InternalMethod not to be Internal*because we want to test the error message*"); } [Fact] @@ -747,7 +747,7 @@ public void When_asserting_a_protected_internal_member_is_not_protected_internal // Assert act.Should().Throw() .WithMessage( - "Expected method ProtectedInternalMethod not to be ProtectedInternal*because we want to test the error message*"); + "Expected method TestClass.ProtectedInternalMethod not to be ProtectedInternal*because we want to test the error message*"); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs index e4479a345..9ca993568 100644 --- a/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs @@ -428,7 +428,7 @@ public void When_asserting_a_readwrite_property_is_not_writable_it_fails_with_us action .Should().Throw() .WithMessage( - "Expected propertyInfo ReadWriteProperty not to have a setter because we want to test the error message."); + "Expected ReadWriteProperty not to have a setter because we want to test the error message."); } [Fact] @@ -444,7 +444,7 @@ public void When_asserting_a_writeonly_property_is_not_writable_it_fails_with_us action .Should().Throw() .WithMessage( - "Expected propertyInfo WriteOnlyProperty not to have a setter because we want to test the error message."); + "Expected WriteOnlyProperty not to have a setter because we want to test the error message."); } [Fact] @@ -459,7 +459,7 @@ public void When_subject_is_null_not_be_writable_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected property not to have a setter *failure message*, but propertyInfo is ."); + .WithMessage("Expected propertyInfo not to have a setter *failure message*, but it is ."); } } @@ -587,7 +587,7 @@ public void When_subject_is_null_be_readable_with_accessmodifier_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected property to be Public *failure message*, but propertyInfo is ."); + .WithMessage("Expected propertyInfo to be Public *failure message*, but it is ."); } [Fact] @@ -652,7 +652,7 @@ public void Do_not_the_check_access_modifier_when_the_property_is_not_writable() // Assert action.Should().Throw() - .WithMessage("Expected propertyInfo ReadOnlyProperty to have a setter."); + .WithMessage("Expected ReadOnlyProperty to have a setter."); } [Fact] @@ -667,7 +667,7 @@ public void When_subject_is_null_be_writable_with_accessmodifier_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected property to be Public *failure message*, but propertyInfo is ."); + .WithMessage("Expected propertyInfo to be Public *failure message*, but it is ."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveIndexer.cs b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveIndexer.cs index 9f3bd5022..bf1b69182 100644 --- a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveIndexer.cs +++ b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveIndexer.cs @@ -23,7 +23,7 @@ public void When_asserting_a_type_has_an_indexer_which_it_does_then_it_succeeds( type.Should() .HaveIndexer(typeof(string), [typeof(string)]) .Which.Should() - .BeWritable(CSharpAccessModifier.Internal) + .BeWritable(CSharpAccessModifier.Public) .And.BeReadable(CSharpAccessModifier.Private); // Assert diff --git a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs index fe7f4cbb6..a7d96a9b0 100644 --- a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs +++ b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs @@ -30,6 +30,21 @@ public void When_asserting_a_type_has_a_property_which_it_does_then_it_succeeds( act.Should().NotThrow(); } + [Fact] + public void The_name_of_the_property_is_passed_to_the_chained_assertion() + { + // Arrange + var type = typeof(ClassWithMembers); + + // Act + Action act = () => type + .Should().HaveProperty(typeof(string), "PrivateWriteProtectedReadProperty") + .Which.Should().NotBeWritable(); + + // Assert + act.Should().Throw("Expected property PrivateWriteProtectedReadProperty not to have a setter."); + } + [Fact] public void When_asserting_a_type_has_a_property_which_it_does_not_it_fails() {