1

I'm new to unit testing, and I wanted to test a method that gets a string array that contains some names like "John,Doe" and then it splits the name by the "," and populates a list of PersonModel with those names, so I expect that there is a PersonModel with first name John, and last name Doe, in the returned list, but the Assert.Contains method keeps throwing this error:

Assert.Contains() Failure

Not found: PersonModel { FirstName = "John", FullName = "John Doe", LastName = "Doe" }

In value: List [PersonModel { FirstName = "John", FullName = "John Doe", LastName = "Doe" }, PersonModel { FirstName = "Jane", FullName = "Jane Doe", LastName = "Doe" }]

This is the method for converting names to people list in the DataAccess class:

public static List<PersonModel> ConvertCsvNamesToPeopleList(string[] csvContent)
{
    List<PersonModel> output = new List<PersonModel>();

    foreach (string line in csvContent)
    {
        string[] data = line.Split(',');
        output.Add(new PersonModel { FirstName = data[0], LastName = data[1] });
    }

    return output;
}

And this is the test:

[Fact]
public void ConvertCsvNamesToPeopleList_ValidCsvContent_ShouldWork()
{
    string[] csvContent = { "John,Doe", "Jane,Doe" };

    var expectedPerson = new PersonModel { FirstName = "John", LastName = "Doe" };
    var expectedPerson2 = new PersonModel { FirstName = "Jane", LastName = "Doe" };
    var actual = DataAccess.ConvertCsvNamesToPeopleList(csvContent);

    Assert.Contains(expectedPerson, actual);
    Assert.Contains(expectedPerson2, actual);
}

The person model:

public class PersonModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName => $"{ FirstName } { LastName }";
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
bzmind
  • 386
  • 3
  • 19
  • Does `PersonModel` override `Equals`? – Mureinik Dec 21 '21 at 16:24
  • @Mureinik No it doesn't – bzmind Dec 21 '21 at 16:26
  • 3
    And `PersonModel` is a `class`, right? Not a `record` or a `struct`? What you're running into is [reference vs value equality](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/equality-comparisons#:~:text=Reference%20equality%20means%20that%20two,shown%20in%20the%20following%20example.&text=In%20this%20code%2C%20two%20objects,refer%20to%20the%20same%20object.). The objects have the same *value*, but are not the same object in memory and so are not equal. – D M Dec 21 '21 at 16:30
  • @DM Yeah it's a class – bzmind Dec 21 '21 at 16:30
  • 1
    Take a look at [Best way to compare two complex objects](https://stackoverflow.com/questions/10454519/best-way-to-compare-two-complex-objects). There are multiple ways to compare class instances by value. As @Mureinik points out, the easiest would be to [override `Equals`](https://learn.microsoft.com/en-us/visualstudio/ide/reference/generate-equals-gethashcode-methods?view=vs-2022). – D M Dec 21 '21 at 16:32
  • @DM Oh, I didn't know anything about that, thanks – bzmind Dec 21 '21 at 16:39
  • 2
    No worries! Worth noting that you get this functionality for free if you're using instances of a [`record`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record) (available in C# 9.0 or newer): [Try it out on .NET Fiddle](https://dotnetfiddle.net/uxDxs2) – D M Dec 21 '21 at 16:48

1 Answers1

3

Contains relies on finding two objects that are equal, so you should override its Equals method. For good measures, you should override its GetHashCode method too, so you can use it as a key of a dictionary if you ever need to:

public override bool Equals(object obj)
{
    return obj is PersonModel model &&
           FirstName == model.FirstName &&
           LastName == model.LastName;
}

public override int GetHashCode()
{
    return HashCode.Combine(FirstName, LastName);
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • To override the `GetHashCode`, I couldn't find `HashCode` anywhere, I tried `System.HashCode` but it's not there anymore, how should I use it? – bzmind Dec 27 '21 at 08:37
  • @Genesys if you don't have `HashCode` available, there are a few ways you can implement `GetHashCode` yourself: https://stackoverflow.com/q/263400/2422776 – Mureinik Dec 27 '21 at 10:51