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
DelegatingConstraintResult does not use its _innerResult
for WriteMessageTo
#4545
Comments
Repro steps: [TestFixture]
public class MyTests
{
[Test]
public void ShouldWriteSameMessage()
{
var actualValue = false;
Assert.Multiple(() =>
{
Assert.That(actualValue, new MyConstraint());
Assert.That(actualValue, new MyConstraint().After(20));
});
}
public class MyConstraint : Constraint
{
public MyConstraint()
{
Description = "Custom description";
}
public override ConstraintResult ApplyTo<TActual>(TActual actual)
{
return new MyConstraintResult(this);
}
}
public class MyConstraintResult : ConstraintResult
{
public override void WriteMessageTo(MessageWriter writer)
{
writer.Write("Custom message");
}
public MyConstraintResult(MyConstraint myConstraint) : base(myConstraint, actualValue: null, ConstraintStatus.Failure)
{
}
}
} The output is not what I'd expect:
The output of the delayed constraint uses the constraint's description, but ignores the custom |
Thanks for the clear repro @metoule . I've uploaded a sample of it to our issues repo: https://github.com/nunit/nunit.issues/tree/main/Issue4545 I can confirm as well that it happens for both NUnit 3.14 and 4.0 beta1 |
@metoule Is this something you'd be interested in submitting a PR to fix? |
I originally wanted to open a PR, because that looked like a quick fix, but it's surprisingly tricky. You can't simply modify the One possible solution, but that would completely change the display of a delayed constraint, is to always output the delay before writing the message. Something like: Before:
After:
|
Thanks for the investigation and analysis @metoule Are you able to explain a bit more about how you came across this issue, and if you've only seen it when used in conjunction with other custom constraints? |
My goal is to wrap the GregFinzer/Compare-Net-Objects library in a NUnit constraint. That library performs a deep equality check, and returns a user friendly output that looks like this:
It makes it easy to compare complex objects in one go, instead of one property at a time, but it's doesn't really match the NUnit assumption that there's an To use it in NUnit, I use the using KellermanSoftware.CompareNetObjects;
using NUnit.Framework.Constraints;
public class CompareNetObjectsConstraint<TExpected> : EqualConstraint
{
private readonly TExpected _expected;
public CompareNetObjectsConstraint(TExpected expected) : base(expected)
{
_expected = expected;
}
public override ConstraintResult ApplyTo<TActual>(TActual actual)
{
var comparisonResult = new CompareLogic().Compare(_expected, actual);
return new CompareNetObjectsConstraintResult<TExpected>(this, actual, comparisonResult);
}
}
public class CompareNetObjectsConstraintResult<TExpected> : EqualConstraintResult
{
private readonly ComparisonResult _comparisonResult;
public CompareNetObjectsConstraintResult(CompareNetObjectsConstraint<TExpected> constraint, object? actual, ComparisonResult comparisonResult)
: base(constraint, actual, comparisonResult.AreEqual)
{
_comparisonResult = comparisonResult;
}
public override void WriteMessageTo(MessageWriter writer)
{
writer.WriteMessageLine($"Showing {_comparisonResult.Differences.Count} differences (max: {_comparisonResult.Config.MaxDifferences})");
foreach (var difference in _comparisonResult.Differences)
{
writer.WriteMessageLine(difference.ToString());
}
}
} For my own purpose, I found a way to make it work though:
That way, I have:
|
Thank you for the thorough repro and information @metoule ! I haven't been able to get to a computer for an extended time to dig into it with the latest info, but I'm glad to hear you've been able to make this work on your end. |
If you intend this for a PR for NUnit, please note that we are not likely to accept 3rd party dependencies, except in a very few cases, and after a deep discussion on the necessity of it. |
@OsirisTerje, thanks for the information. Rest assured that I only want to create a new constraint for my own use: this is quite niche and certainly not something I'd even consider being included in NUnit. I just wanted to illustrate how I use the |
I've had a bit less time than expected lately but I've tried taking a look at this a few times now to try to understand it. I think the issue here may be a general extensibility one. Most of NUnit's built-in constraints operate or compose together by appending lines to the output but the I think @metoule 's idea here to change the output to have the "delay" information appear on its own line is a good solution to the problem if we wanted to pursue it. @nunit/framework-team what are your thoughts? |
Sounds good to me |
The
DelegatingConstraintResult
should override theWriteMessageTo
to call the_innerResult.WriteMessageTo
method, otherwise it's not writing the expected message to the output.WriteActualValueTo
andWriteAdditionalLinesTo
are already properly forwarded to the_innerResult
.nunit/src/NUnitFramework/framework/Constraints/DelayedConstraint.cs
Lines 369 to 382 in 6a89737
The text was updated successfully, but these errors were encountered: