Search code examples
c#.netunit-testingnunit

NUnit: assert that a collection contains an element that matches some arbitrary condition


Not the best title, so let me give an example

I have a method that converts a list of objects into another type. Say I have the following classes

class Person {
  string Name {get;set;}
  int Age {get;set;}
  string Uuid {get;set;}
}

class OtherPerson {
  string FirstName {get;set;}
  string LastName {get;set;}
  DateTime DateOfBirth {get;set;}
  Guid Guid {get;set;}
}

My method converts a List<OtherPerson> into a List<Person>. I want to write an assertion that, for each element in the input, the output contains an element matching a generic equivalency test. For instance

[Test]
public void MyTest(){
  var person1 = GenerateOtherPerson();

  List<Person> result = _classUnderTest.ConverToPerson(new[]{person1}.ToList());

  //This is what I'm looking for
  Assert.That(result, Contains.Item.Matching(p => AreEquivalent(p, person1)));
}

public bool AreEquivalent(Person p1, OtherPerson p2) {
  return p1.Name == $"{p2.FirstName} {p2.LastName}"
    && p1.Age == DateTime.Now.Year - p2.DateOfBirth.Year
    && p1.Uuid == p2.Guid.ToString();
}

Note that this is a simplistic example of looking for only 1 element, but you can easily imagine an extension to this in which I have several OtherPersons and I want to assert that the result of the method contains exactly 1 element matching each of the inputs.

I'm having trouble finding an idiomatic assertion for this. The best I can come up with is

matches = result.Select(p => AreEquivalent(p, person1));
Assert.That(matches, Contains.Item(true));

But I'm not a fan of that. I generally like writing assertions that operate directly on the result of my method, rather than writing code that converts the result into some other form and asserting on that.

Is what I'm looking for possible?


Solution

  • Try this...

    Assert.That(result, Has.Some.Matches(new Predicate<Person>(p => { return AreEquivalent(p, person1); }));
    

    or if you prefer use Has.Exactly(1).Matches(..., which will go through the entire list making sure there are no others that match.

    This is actually intended to work (and did work when I first wrote it) but does no longer, probably due to overloads that have been added...

    Assert.That(result, Has.Some.Matches(p => AreEquivalent(p, person1)));
    

    I couldn't make it compile without the new, return and braces. :-( Maybe you can find a better syntax that works.

    BTW, this is the PredicateConstraint, which you can find in the NUnit source code but unfortunately not in the docs. There's even one test of it in NUnit's own tests.