New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ShouldBeEquivalentTo case insensitive property match #254
Comments
Not out of the box. But what you can do is implement IMemberMatchingRule and pass that to the |
Any progress on this? I feel like it's a common enough scenario that it would be nice to have an out-of-the-box solution for it. Also the link provided above is broken :( |
I've just needed the same thing. If it helps anyone, here's my implementation that ignores case when matching properties.
public class CaseInsensitiveMatch : IMemberMatchingRule
{
public SelectedMemberInfo Match(
SelectedMemberInfo expectedMember,
object subject,
string memberPath,
IEquivalencyAssertionOptions config)
{
List<PropertyInfo> properties =
subject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(pi => pi.Name.Equals(expectedMember.Name, StringComparison.OrdinalIgnoreCase))
.ToList();
PropertyInfo property = properties.Count > 1
? properties.SingleOrDefault(p => p.PropertyType == expectedMember.MemberType)
: properties.SingleOrDefault();
SelectedMemberInfo match = SelectedMemberInfo.Create(property);
return match;
}
} |
Cool, thanks for the code @JSkimming :) I also went with my own implementation that I'll share here. I chose to just not use the actualStringList.Should().Equal(expectedStringList, (s1, s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase)); |
I've been thinking on this for some time and can't come up with a case where this could be really useful, as usually you won't compare two object of different types. You create an However, if this feature is to be implemented, I'd like to have an ability to perform case-insensitive matches not just considering letters in lower and upper case equivalent, but also ignoring the differences in naming conventions like
Since cases like |
@rynkevich the use case is when I want to compare two objects where I either can't or don't want to change one to be consistent with the other. For instance, if I'm using a third-party library to retrieve some data, and then save it to an Entity Framework DB Context. The third part library has an object with the properties I could:
I prefer option 2 because I don't want to propagate the inconsistent naming convention to my models, and it is less work to maintain the tests than option 3. BTW, I usually adopt option 3 where the source and destination objects are suitably different. But for a simple case where only the case is different, option 2 makes for simpler and more maintainable tests. I can also imagine a scenario where the third party library names things with underscores (I've found this some generated third party libraries that are built on frameworks that use the underscore convention). I agree that it would also be useful. But the simple case adding an option to enable case insensitive appears, on the surface, to be a relatively simple option to allow. |
@JSkimming, thanks for the example. I guess, it's all about just providing a more convenient API for specific usage scenarios. The question is don't we try to make FluentAssertions perform more and more complex mapping logic? This isn't AutoMapper or something. @dennisdoomen, could you share your thoughts on that? Speaking about third party libraries with strange conventions, an example that comes to my mind is PayPal SDK. They use underscores for property names and that could be where this feature could be applied, if you write some abstration. |
I think it's a fair question to ask for something like that, but considering its edgy scenarios, I don't think it really belongs in the library. So I would not reject a PR that tries to implement it, but I also don't feel the need to do this myself. |
Its fair to say you should not have to implement it but I don't agree that it doesn't belong. As the .net landscape has changed heavily to open source, we are going to have collisions with 3rd party interfaces and coding styles. I don't think it would be out of character for this highly utilitarian library to avoid being opinionated and not put the burden on the developer to find a work around for this casing problem. Sending a PR into highly developed library is always intimidating. I am will to give it a shot but @dennisdoomen public static class CaseInsensitivePropertyMatchExtensions
{
public static EquivalencyAssertionOptions<T> CaseInsensitiveProperties<T>(this EquivalencyAssertionOptions<T> options)
{
options.Using(new CaseInsensitivePropertyMatch());
return options;
}
}
public sealed class CaseInsensitivePropertyMatch : IMemberMatchingRule
{
public SelectedMemberInfo Match(
SelectedMemberInfo expectedMember,
object subject,
string memberPath,
IEquivalencyAssertionOptions config)
{
var subjectType = subject.GetType();
var expectedKey = $"{subjectType.FullName}.{expectedMember.Name}";
if (KeyedMembers.TryGetValue(expectedKey, out var selectedMemberInfo))
return selectedMemberInfo;
var properties = subjectType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToList();
foreach (var property in properties)
{
var propertyKey = $"{subjectType.FullName}.{property.Name}";
KeyedMembers.TryAdd(propertyKey, SelectedMemberInfo.Create(property));
}
if (KeyedMembers.TryGetValue(expectedKey, out selectedMemberInfo))
return selectedMemberInfo;
throw new InvalidOperationException($"Failed to find member {expectedMember.Name} in {expectedMember.DeclaringType.FullName} for case-insensitive member equivalency match.");
}
private static readonly ConcurrentDictionary<string, SelectedMemberInfo> KeyedMembers = new ConcurrentDictionary<string, SelectedMemberInfo>(StringComparer.OrdinalIgnoreCase);
} |
@nick-randal, here's my two cents:
|
Yep. the direction is fine and the remarks from @rynkevich are completely valid. |
This is solved by #1742 |
How can I use ShouldBeEquivalentTo case insensitive property match. i.e. A.PersonId and B.PersonID.
The text was updated successfully, but these errors were encountered: