断言列表中字段的唯一性

Assert uniqueness of fields in list

我在C#中做了一个列表,我想测试一下Id字段的所有值是否都是唯一的。

public static List<RestaurantReview> _reviews = new List<RestaurantReview>
{
    new RestaurantReview
    {
        Id = 1,
        Name = "McDonalds",
    },
    new RestaurantReview
    {
        Id = 2,
        Name = "Burger King",
    },
}

因为我进行了一些调试,所以我发现它是 运行 通过列表但我没有得到正确的测试值。有人可以解释一下我在这里做错了什么吗?

[TestMethod()]
public void CheckReviewIds()
{
    var FirstReview = ReviewsController._reviews.First();
    bool AllUniqueIds = ReviewsController._reviews.All(s => s.Id == FirstReview.Id);

    Assert.IsFalse(AllUniqueIds);

}

提前致谢。

您正在检查是否所有值都不等于第一个值。如果值例如 [1, 2, 3, 3]、none 等于第一个但本身等于 3 == 3,则可能是这种情况。

相反,您可以 GroupBy 按值对它们进行分组,然后检查它们是否不同。我假设性能在这里不是一个大问题(如果列表少于我假设的 100000 个项目就是这种情况):

 ReviewsController._reviews.GroupBy(x => x.Id).Count() == ReviewsController._reviews.Count; 

请注意,测试组件的内部状态可能不是最好的主意,而是测试它公开的 API。否则,您通过单元测试定义的契约将受限于您的实现细节。不过最后一段只是个人意见。

另一种在时间方面具有更好性能的解决方案(特别是如果您拥有的数据集很大)是使用这样的 HashSet

bool IsAllUnique<T>(IEnumerable<T> values)
{
    HashSet<T> hash_set = new HashSet<T>();

    return values.All(x => hash_set.Add(x));
}

然后你可以这样使用它:

bool unique = IsAllUnique(ReviewsController._reviews.Select(x => x.Id));

此解决方案取决于以下事实:如果我们尝试添加的值已经存在,HashSet.Add 将 return 为 false。

此解决方案具有更好性能的一个原因是,如果它检测到重复项目,则不必遍历列表中的其余项目。

我发现验证这一点的最简单方法是计算所有不同的值并将它们与实际值的数量进行比较:

var actual = ReviewsController._reviews.Select(r => r.Id).Distinct().Count();

var expected = ReviewsController._reviews.Count();
Assert.Equal(expected, actual);