From 8afc473bcde8e11ebf2939697bd0d69aa84402a4 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Jan 2023 12:52:49 +0100 Subject: [PATCH] Extend `ThatArePublicOrInternal` to also look at the setter of properties (#2082) Co-authored-by: IT-VBFK <49762557+IT-VBFK@users.noreply.github.com> --- .../Types/PropertyInfoSelector.cs | 6 +- .../Types/PropertyInfoSelectorSpecs.cs | 87 +++++++++++++++---- docs/_pages/releases.md | 1 + 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/Src/FluentAssertions/Types/PropertyInfoSelector.cs b/Src/FluentAssertions/Types/PropertyInfoSelector.cs index 4c4f9ec6cb..1fb87c13be 100644 --- a/Src/FluentAssertions/Types/PropertyInfoSelector.cs +++ b/Src/FluentAssertions/Types/PropertyInfoSelector.cs @@ -40,7 +40,7 @@ public PropertyInfoSelector(IEnumerable types) } /// - /// Only select the properties that have a public or internal getter. + /// Only select the properties that have at least one public or internal accessor /// public PropertyInfoSelector ThatArePublicOrInternal { @@ -48,8 +48,8 @@ public PropertyInfoSelector ThatArePublicOrInternal { selectedProperties = selectedProperties.Where(property => { - MethodInfo getter = property.GetGetMethod(nonPublic: true); - return (getter is not null) && (getter.IsPublic || getter.IsAssembly); + return property.GetGetMethod(nonPublic: true) is { IsPublic: true } or { IsAssembly: true } + || property.GetSetMethod(nonPublic: true) is { IsPublic: true } or { IsAssembly: true }; }); return this; diff --git a/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs b/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs index b20b6535ff..dd612bb0fd 100644 --- a/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs +++ b/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using FluentAssertions.Types; using Internal.Main.Test; @@ -216,23 +215,6 @@ public void When_selecting_methods_that_do_not_return_a_specific_type_it_should_ properties.Should().HaveCount(4); } - [Fact] - public void When_combining_filters_to_filter_methods_it_should_return_only_the_applicable_methods() - { - // Arrange - Type type = typeof(TestClassForPropertySelector); - - // Act - IEnumerable properties = type.Properties() - .ThatArePublicOrInternal - .OfType() - .ThatAreDecoratedWith() - .ToArray(); - - // Assert - properties.Should().ContainSingle(); - } - [Fact] public void When_selecting_properties_decorated_with_an_inheritable_attribute_it_should_only_return_the_applicable_properties() { @@ -355,6 +337,74 @@ public void When_selecting_properties_return_types_it_should_return_the_correct_ , typeof(string), typeof(int), typeof(int), typeof(int), typeof(int) }); } + + public class ThatArePublicOrInternal + { + [Fact] + public void When_combining_filters_to_filter_methods_it_should_return_only_the_applicable_methods() + { + // Arrange + Type type = typeof(TestClassForPropertySelector); + + // Act + IEnumerable properties = type.Properties() + .ThatArePublicOrInternal + .OfType() + .ThatAreDecoratedWith() + .ToArray(); + + // Assert + properties.Should().ContainSingle(); + } + + [Fact] + public void When_a_property_only_has_a_public_setter_it_should_be_included_in_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPublicSetter); + + // Act + IEnumerable properties = type.Properties().ThatArePublicOrInternal.ToArray(); + + // Assert + properties.Should().HaveCount(3); + } + + private class TestClassForPublicSetter + { + private static string myPrivateStaticStringField; + + public static string PublicStaticStringProperty { set => myPrivateStaticStringField = value; } + + public static string InternalStaticStringProperty { get; set; } + + public int PublicIntProperty { get; init; } + } + + [Fact] + public void When_selecting_properties_with_at_least_one_accessor_being_private_should_return_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPrivateAccessors); + + // Act + IEnumerable properties = type.Properties().ThatArePublicOrInternal.ToArray(); + + // Assert + properties.Should().HaveCount(4); + } + + private class TestClassForPrivateAccessors + { + public bool PublicBoolPrivateGet { private get; set; } + + public bool PublicBoolPrivateSet { get; private set; } + + internal bool InternalBoolPrivateGet { private get; set; } + + internal bool InternalBoolPrivateSet { get; private set; } + } + } } #region Internal classes used in unit tests @@ -455,5 +505,4 @@ public DummyPropertyAttribute(string value) public string Value { get; private set; } } - #endregion diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index 7996def235..feabddad4e 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -22,6 +22,7 @@ sidebar: * Added new extension methods to be able to write `Exactly.Times(n)`, `AtLeast.Times(n)` and `AtMost.Times(n)` in a more fluent way - [#2047](https://github.com/fluentassertions/fluentassertions/pull/2047) ### Fixes +* `PropertyInfoSelector.ThatArePublicOrInternal` now takes the setter into account when determining if a property is `public` or `internal` - [#2082] (https://github.com/fluentassertions/fluentassertions/pull/2082) * Quering properties on classes, e.g. `typeof(MyClass).Properties()`, now also includes static properties - [#2054](https://github.com/fluentassertions/fluentassertions/pull/2054) * Nested AssertionScopes now print the inner scope reportables - [#2044](https://github.com/fluentassertions/fluentassertions/pull/2044) * Throw `ArgumentException` instead of `ArgumentNullException` when a required `string` argument is empty - [#2023](https://github.com/fluentassertions/fluentassertions/pull/2023)