简体   繁体   中英

Assert that value is equal to any of a collection of expected values

Does NUnit provide a constraint to find whether the actual value is the element of a given enumerable or array, in other words, that it is equal to any of multiple expected values? Something like:

Assert.That(actual, Is.EqualToAnyOf(new[] { 1, 2, 3 }))

That is, to point out, the actual is a single value. I expect the value to be either 1 , 2 , or 3 . The assertion

Assert.That(actual, Contains.Element(expected))

checks logically the same, but it is the opposite intention: Here we have a collection of actual values and expect one value to be in it.

Furthermore, I found these but they all don't fit:

Assert.That(actual, Is.EqualTo(expected)) // only allows one value
Assert.That(actual, Is.InRange(start, end)) // only works for consecutive numbers
Assert.That(actual, Is.SubsetOf(expected)) // only works if actual is an enumerable
Assert.That(expected.Contains(actual)) // meaningless "expected: true but was: false" message

CollectionAssert should be what you need if I am not overlooking something. It is as simple as:

CollectionAssert.Contains(IEnumerable expected, object actual);

However, there seems to be several ways to achieve your goal, such as:

[Test]
public void CollectionContains()
{
    var expected = new List<int> { 0, 1, 2, 3, 5 };
    var actual = 5;

    CollectionAssert.Contains(expected, actual);
    Assert.That(expected, Contains.Item(actual));
}

Above assertions should basically assert the same and could be used interchangeably.

Edit: Question was modified, stating that Assert.That(expected, Contains.Item(actual)); is not valid even though it logically tests the same thing.

There is a way to do this built in to NUnit, using the Or constraint:

Assert.That(actual, Is.EqualTo(1).Or.EqualTo(2).Or.EqualTo(3))

If your list is more dynamic, you can build your list of Or s like this:

var expected = new[] { 1, 2, 3 };
var constraints = Is.EqualTo(expected[0]);

for(var i = 1; i < expected.Length; i++)
    constraints = constraints.Or.EqualTo(expected[i]);

Assert.That(actual, constraints);

That latter answer doesn't read as well in the fluid syntax, but does achieve the dynamic building of or constraints. You could probably wrap that in a custom constraint as patrick-quirk demonstrated in order to achieve a more readbale fluid syntax, but that's up to you.

The only way I could see to accomplish this is by creating your own constraint. It's pretty straightforward to do though.

The constraint class itself:

public class OneOfValuesConstraint : EqualConstraint
{
    readonly ICollection expected;
    NUnitEqualityComparer comparer = new NUnitEqualityComparer();

    public OneOfValuesConstraint(ICollection expected)
        : base(expected)
    {
        this.expected = expected;
    }

    public override bool Matches(object actual)
    {
        // set the base class value so it appears in the error message
        this.actual = actual;
        Tolerance tolerance = Tolerance.Empty;

        // Loop through the expected values and return true on first match
        foreach (object value in expected)
            if (comparer.AreEqual(value, actual, ref tolerance))
                return true;

        // No matches, return false
        return false;
    }

    // Overridden for a cleaner error message (contributed by @chiccodoro)
    public override void WriteMessageTo(MessageWriter writer)
    {
        writer.DisplayDifferences(this);
    }

    public override void WriteDescriptionTo(MessageWriter writer)
    {
        writer.Write("either of ");
        writer.WriteExpectedValue(this.expected);
    }
}

And to make it fluent, create a static method to wrap it (contributed by @chicodorro):

public static class IsEqual
{
    public static OneOfValuesConstraint ToAny(ICollection expected)
    {
        return new OneOfValuesConstraint(expected);
    }
}    

Then to use it:

int[] expectedValues = new[] { 0, 1, 2 };
Assert.That(6, IsEqual.ToAny(expectedValues));

Fails with the message:

Expected: either of < 0, 1, 2 >

But was: 6

I know this is an old question, bu I have a maybe better (and native) suggestion. With NUnit 3.x (I'm on 3.10.1) you can use Is.AnyOf :

 Assert.That(
actualValue, 
Is.AnyOf(expectedValue1, expectedValue2, expectedValue3),
"My error message");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM