80

I have two classes:

public class ClassA
{
  public int? ID {get; set;}
  public IEnumerable<ClassB> Children {get; set;}
}

public class ClassB
{
  public int? ID {get; set;}
  public string Name {get; set;}
}

I want to use fluent assertions to compare to ClassA instances. However I want to ignore the IDs (because the IDs will have been assigned after the save).

I know I can do this:

expectedA.ShouldBeEquivalentTo(actualA, options => options.Excluding(x => x.PropertyPath == "Children[0].ID"));

Which I can obviously repeat for each ClassB in the collection. However I'm looking for a way to exclude the all the IDs (rather than doing an exclude for each element).

I've read this question however if I remove the [0] indexers the assertions fail.

Is this possible?

Community
  • 1
  • 1
Liath
  • 9,913
  • 9
  • 51
  • 81

11 Answers11

68

What about?

expected.ShouldBeEquivalentTo(actualA, options => options.Excluding(su => 
   (su.RuntimeType == typeof(ClassB)) && (su.PropertyPath.EndsWith("Id")));`

Or you could do a RegEx match on the property path, such as

expected.ShouldBeEquivalentTo(actualA, options => options.Excluding(su => (Regex.IsMatch
   ("Children\[.+\]\.ID"));

I actually like that last one, but the regex stuff makes it a bit difficult to read. Maybe I should extend ISubjectInfo with a method to match the path against a wildcard pattern, so that you can do this:

expected.ShouldBeEquivalentTo(actualA, options => options
  .Excluding(su => su.PathMatches("Children[*].ID")));
superjos
  • 12,189
  • 6
  • 89
  • 134
Dennis Doomen
  • 8,368
  • 1
  • 32
  • 44
  • I'm going to mark this one as the answer as the regex in an extension method is the approach I went with in the end – Liath Mar 05 '14 at 06:22
  • 3
    How has this changed in more recent versions of FluentAssertions? I'm not sure `PropertyPath` is still there – superjos Feb 14 '17 at 09:52
  • 1
    I tried both options without success, but was able to fix RegEx option to work with current version - see https://stackoverflow.com/questions/22142576/how-to-use-exclude-in-fluentassertions-for-property-in-collection/51858245#51858245 . By the way, the new name for PropertyPath is SelectedMemberPath – Michael Freidgeim Aug 15 '18 at 12:00
  • 2
    `Regex.IsMatch(x.SelectedMemberPath, @"Children\[\d+\]\.ID")` – Saro Taşciyan Aug 08 '19 at 20:30
  • 1
    FluentAssertions v6 removed SelectedMemberPath the solution here is: options => options.Excluding((IMemberInfo x) => x.DeclaringType == typeof(ClassB) && x.Path.EndsWith("Id")) – Lakedaimon Aug 13 '21 at 13:06
  • `SelectedMemeberPath` was renamed to `Path` in v.6.0.0: https://fluentassertions.com/releases/#600 – xavier Apr 08 '22 at 12:27
39

I've just come across a similar problem and the latest version of FluentAssertions has changed things a bit.

My objects contains dictionaries of other objects. The objects in the dictionaries contain other objects that I want to exclude. The scenario I have is around testing Json serialization where I ignore certain properties.

This works for me:

gotA.ShouldBeEquivalentTo(expectedB , config => 
  config
    .Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Venue))
    .Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Exhibit))
    .Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Content))
    .Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Survey))
    .Excluding(ctx => ctx.SelectedMemberInfo.MemberType == typeof(Media))
  );

Took some time to work out how to do it, but it's really useful!

Nick Randell
  • 17,805
  • 18
  • 59
  • 74
  • 2
    For what it is worth, you can also pass in an anonymous object as the expectation since FA 5 and include only the properties you care about. – Dennis Doomen Aug 10 '19 at 08:21
19

This is supported by FluentAssertions 6.7

actualA.Should().BeEquivalentTo(expectedA, options =>
    options
       .For(a => a.Children)
       .Exclude(b => b.ID));
Bouke Versteegh
  • 4,097
  • 1
  • 39
  • 35
16

Simple way would be to set assertions on collection directly, combined with its exclusion on ClassA equivalency assertion:

expectedA.ShouldBeEquivalentTo(expectedB,
   o => o.Excluding(s => s.PropertyInfo.Name == "Children"));
expectedA.Children.ShouldBeEquivalentTo(expectedB.Children,
   o => o.Excluding(s => s.PropertyInfo.Name = "Id"));
k.m
  • 30,794
  • 10
  • 62
  • 86
10

There are a few valid answers here, but I am adding another one that does not involve stringly-typed expressions.

expectedA.ShouldBeEquivalentTo(expectedB, o => o.Excluding(s => s.Children));
expectedA.Children.ShouldBeEquivalentTo(expectedB.Children, o => o.Excluding(s => s.Id));
Aleksei
  • 562
  • 4
  • 11
6

The ShouldBeEquivalentTo method seems to be obsolete now, in order to get path for the accepted answer you can use the Excluding overload with IMemberInfo.SelectedMemberPath instead:

expected.Should().BeEquivalentTo(actualA, options => 
    options.Excluding((IMemberInfo mi) => mi.SelectedMemberPath.EndsWith("ID")));
wondra
  • 3,271
  • 3
  • 29
  • 48
4

Based on RegEx match idea from Dennis Doomen‘s answer I was able to make it working

expected.ShouldBeEquivalentTo(actualA, options =>
  options.Excluding(su => 
     (Regex.IsMatch(su.SelectedMemberPath, "Children\\[.+\\].ID"));

Difference with Dennis answer: passing su.SelectedMemberPath, double back slashes to escape square brackets.

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
  • 5
    `SelectedMemeberPath` was renamed to `Path` in v.6.0.0 https://fluentassertions.com/releases/#600 – xavier Apr 08 '22 at 12:28
4
actual.Should().BeEquivalentTo(expected,
  assertionOptions => assertionOptions
    .Excluding(x => x.CreationTimestamp))

BUT if you work with structs and class overriding equals, then you should change the default comparing with ComparingByMembers https://fluentassertions.com/objectgraphs/#value-types

actual.Should().BeEquivalentTo(expected,
  assertionOptions => assertionOptions
    .Excluding(x => x.CreationTimestamp)
    .ComparingByMembers<T>())
Tamás Lévai
  • 325
  • 3
  • 6
1

The easiest way is:

expected.ShouldBeEquivalentTo(actual, config => config.ExcludingMissingMembers());
cSteusloff
  • 2,487
  • 7
  • 30
  • 51
1

I thinks the syntax is something like

       actual.Should().BeEquivalentTo(
        expected, 
        config => config.Excluding(o => o.Id).Excluding(o => o.CreateDateUtc) });
0

An extension class where you can pass a list of expressions

public static class FluentAssertionsExtensions {
    public static EquivalencyAssertionOptions<T> ExcludingNextProperties<T>(
        this EquivalencyAssertionOptions<T> options,
        params Expression<Func<T, object>>[] expressions) {
        foreach (var expression in expressions) {
            options.Excluding(expression);
        }

        return options;
    }
}

Usage

actual.Should().BeEquivalentTo(expected, 
            config => config.ExcludingNextProperties(
                o => o.Id, 
                o => o.CreateDateUtc))
Artiom
  • 7,694
  • 3
  • 38
  • 45