Reputation: 9546
As described in this question, I'm working on a method that returns the elements from one List<FileInfo>
that are not present in another List<FileInfo>
. I've implemented Nawfal's solution as follows:
public List<FileInfo> SourceNotInDest(List<FileInfo> SourceFiles, List<FileInfo> DestFiles)
{
var notInDest = SourceFiles.Where(c => !DestFiles.Any(p => p.Name == c.Name)).ToList();
return notInDest;
}
My data set for SourceFiles is:
u:\folder1\a.txt
u:\folder1\b.txt
u:\folder1\c.txt
u:\folder1\d.txt
DestFiles is:
u:\folder2\a.txt
u:\folder2\b.txt
u:\folder2\c.txt
When I step through the code and examine the lists' values, this appears to return the expected result. But the unit test fails with the following code:
public void SourceNotInDestTest()
{
//arrange
FileListComparer flc = new FileListComparer(); //class that has the list compare method
FolderReader fr = new FolderReader(); //class for getting FileInfo from folder
List<FileInfo> expectedResult = new List<FileInfo>();
expectedResult.Add(new FileInfo(@"U:\folder1\d.txt"));
List<FileInfo> SourceFiles = fr.fileList(@"U:\folder1"); //gets the FileInfo for each file in the folder
List<FileInfo> DestFiles = fr.fileList(@"U:\folder2");
//act
List<FileInfo> result = flc.SourceNotInDest(FTPFiles, LocalFiles);
//assert
CollectionAssert.AreEquivalent(result, expectedResult);
}
Even though result
and expectedResult
have the same contents--both lists have one element with the same file path and same other properties--the test fails with the message:
CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s)
of <U:\folder1\d.txt>. The actual collection contains 0 occurrence(s).
expectedResult
does have an occurrence of U:\folder1\d.txt
, though. I was thinking maybe the problem is that I'm comparing the memory addresses for two objects instead of the contents of those objects, but I thought the AreEquivalent()
was comparing properties. Is that not the case?
EDIT: Based on the advice about comparing properties instead of addresses, I used this Assert instead, which allowed the test to pass:
foreach (FileInfo fi1 in result)
{
Assert.IsNotNull(expectedResult.Find(fi2 => fi2.FullName == fi1.FullName));
}
foreach (FileInfo fi1 in expectedResult)
{
Assert.IsNotNull(result.Find(fi2 => fi2.FullName == fi1.FullName));
}
Upvotes: 5
Views: 1852
Reputation: 6408
I can suggest 2 approaches which are better than your one in my opinion :)
Select 2 collections of file names and compare these collections:
CollectionAssert.AreEquivalent(
result.Select(fi => fi.FullName).ToArray(),
expectedResult.Select(fi => fi.FullName).ToArray()
);
// ToArray() is added just for better output when test fails.
Use user-defined comparer and compare FileInfo
lists:
Assert.That(
result,
Is
.EquivalentTo(expectedResult)
.Using((Comparison<FileInfo>)((fi1, fi2) => fi1.FullName.CompareTo(fi2.FullName)))
);
Your current implementaion with two foreach loops won't fail in the following case:
result =
u:\folder1\a.txt
u:\folder1\a.txt
expectedResult =
u:\folder1\a.txt
Yeah, it doesn't seem to be real case for files list, but generaly it is not a good idea to replace AreEquivalent()
/Is.EquivalentTo()
with two loops.
Upvotes: 1
Reputation: 9837
The test is failing because your collections have different objects in them. If you have 2 instances of the FileInfo
class that refer to the same file, and you call instanceA.Equals(instanceB)
, the result is false
.
If you could change your code to use strings instead of FileInfo
s, it would work as you expect it to.
Upvotes: 4
Reputation: 8459
Probably because FileInfo
is a reference type and the default comparer just checks for the two elements' addresses to be equals. Since FileInfo
is sealed, you can't derive from it and override the equality comparers. The best option, in my opinion, would be writing your own collection comparer method (since you can't pass an IEqualityComparer
instance to CollectionAssert.AreEquivalent
).
Upvotes: 7