-
Notifications
You must be signed in to change notification settings - Fork 731
Closed
Labels
Description
Description
The class FluentAssertions.Formatting.PredicateLambdaExpressionValueFormatter throws an exception when an assertion fails between two lambda expressions that initialize their members from a parameter.
Reproduction Steps
using FluentAssertions;
using System.Linq.Expressions;
using Xunit;
namespace FluentAssertionTests;
public class FluentAssertionExceptionTest
{
[Fact]
public void BeEquivalentTo_WithEqualityComparer_ShouldNotThrowInvalidOperationException()
{
// Arrange
Expression a = (string arg) => new TestItem { Value = arg };
Expression b = (string arg) => new TestItem { Value = arg };
// Act
a.Should().BeEquivalentTo(b, options => options.Using(new ExpressionEqualityComparer()));
}
private record TestItem
{
public string? Value { get; set; }
}
private class ExpressionEqualityComparer : IEqualityComparer<Expression>
{
public bool Equals(Expression? x, Expression? y) => false;
public int GetHashCode(Expression obj) => default;
}
}Expected behavior
Xunit.Sdk.XunitException
Expected a to be equal to TestItem { Value = arg } according to "FluentAssertionTests.FluentAssertionExceptionTest+ExpressionEqualityComparer", but TestItem { Value = arg } was not.
Actual behavior
System.InvalidOperationException
When called from 'VisitMemberInit', rewriting a node of type 'System.Linq.Expressions.NewExpression' must return a non-null value of the same type. Alternatively, override 'VisitMemberInit' and change it to not visit children of this type.
at System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](T node, String callerName)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
at FluentAssertions.Formatting.PredicateLambdaExpressionValueFormatter.ConstantSubExpressionReductionVisitor.Visit(Expression node)
at FluentAssertions.Formatting.PredicateLambdaExpressionValueFormatter.ReduceConstantSubExpressions(Expression expression)
at FluentAssertions.Formatting.PredicateLambdaExpressionValueFormatter.Format(Object value, FormattedObjectGraph formattedGraph, FormattingContext context, FormatChild formatChild)
at FluentAssertions.Formatting.Formatter.Format(Object value, FormattedObjectGraph output, FormattingContext context, FormatChild formatChild)
at FluentAssertions.Formatting.Formatter.ToString(Object value, FormattingOptions options)
at FluentAssertions.Execution.MessageBuilder.<FormatArgumentPlaceholders>b__6_0(Object a)
at System.Linq.Enumerable.SelectArrayIterator`2.ToArray()
at FluentAssertions.Execution.MessageBuilder.FormatArgumentPlaceholders(String failureMessage, Object[] failureArgs)
at FluentAssertions.Execution.MessageBuilder.Build(String message, Object[] messageArgs, String reason, ContextDataItems contextData, String identifier, String fallbackIdentifier)
at FluentAssertions.Execution.AssertionScope.<>c__DisplayClass38_0.<FailWith>b__0()
at FluentAssertions.Execution.AssertionScope.FailWith(Func`1 failReasonFunc)
at FluentAssertions.Execution.AssertionScope.FailWith(Func`1 failReasonFunc)
at FluentAssertions.Execution.AssertionScope.FailWith(String message, Object[] args)
at FluentAssertions.Execution.GivenSelector`1.FailWith(String message, Object[] args)
at FluentAssertions.Equivalency.Steps.EqualityComparerEquivalencyStep`1.Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
at FluentAssertions.Equivalency.Steps.RunAllUserStepsEquivalencyStep.Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
at FluentAssertions.Equivalency.EquivalencyValidator.RunStepsUntilEquivalencyIsProven(Comparands comparands, IEquivalencyValidationContext context)
at FluentAssertions.Equivalency.EquivalencyValidator.RecursivelyAssertEquality(Comparands comparands, IEquivalencyValidationContext context)
at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(Comparands comparands, EquivalencyValidationContext context)
at FluentAssertions.Primitives.ObjectAssertions`2.BeEquivalentTo[TExpectation](TExpectation expectation, Func`2 config, String because, Object[] becauseArgs)
at FluentAssertionTests.FluentAssertionExceptionTest.BeEquivalentTo_WithEqualityComparer_ShouldNotThrowInvalidOperationException()
Regression?
No response
Known Workarounds
Create a custom ValueFormatter to ignore the default PredicateLambdaExpressionValueFormatter:
public class FluentAssertionExceptionTest
{
public FluentAssertionExceptionTest()
{
Formatter.AddFormatter(new LambdaExpressionFormatter());
}
private class LambdaExpressionFormatter : DefaultValueFormatter
{
public override bool CanHandle(object value) => value is LambdaExpression;
}
...
}Configuration
.NET 7.0.100
FluentAssertions 6.10.0
Other information
The exception seems to be because the NewExpression under an InitMemberExpression doesn't contain any ParameterExpression and is considered a constant.