I have a suite of unit tests that are running quite slowly because they all do something like this:
Assert.AreEqual(expected, actual, message: getDetailedErrorContext(expected, Actual));
when profiling unit tests, most time is spent generating these detailed error context messages that never end up getting used, because tests are passing.
Does Microsoft.VisualStudio.TestTools.UnitTesting
inherently support lazily-generated error messages? I noticed all test methods have an overload e.g.:
Assert.AreEqual(expected, actual, message, params object[] parameters);
Which lets me pass objects into a format string, but I'm not clear on how I can use that when e.g. the message is generated as something like
Assert.AreEqual(expected.Count, actual.Count, "All the items in the list are: "
String.Join(", ", actual.Select(o => $"[{o.Id}: {o.Description}]"));
Is there some existing class I can use that will evaluate to my desired message if (and only if) the test assertion fails? Ideally I'd like to have a solution like:
Assert.AreEqual(expected, actual, () => getDetailedErrorContext(expected, Actual));
Where the lambda is never evaluated if the assertion doesn't fail.
Note, I've considered a solution like replacing all assertion methods with e.g.:
if (expected != actual)
Assert.Fail(getDetailedErrorContext(expected, Actual));
But I don't like this for a few reasons (such as not wanting to replicate things like Assert.AreEqual<double>(
's build-in delta tolerance, and overall difficulty this would create in refactoring our test suite.)
CodePudding user response:
So far, for lack of a more obvious solution, I've created the following wrapper around Lazy<T>
whose only difference is that it will actually resolve the value when ToString()
is called:
public class LazyString : Lazy<string>
{
public LazyString(Func<string> stringBuilder) : base(stringBuilder) { }
public override string ToString() => Value;
}
I can then use this as:
Assert.AreEqual(expected, actual, "{0}", new LazyString(() =>
getDetailedErrorContext(expected, Actual)));
It's a bit ugly and error prone (if one forgets to insert the format string "{0}"
, but it works and is the best I could come up. Sadly the Assert
class is sealed, so I can't even cleanly extend it with my own overloads.