LINQ Contains 语句在复杂对象上用于字符串列表时表现不同

LINQ Contains statement behaves differently when used on a complex object to list of strings

我是白痴吗?这是一个模拟存储库:

Mock<IRepository> repMock = new Mock<IRepository>();
Quote q1 = new Quote { QuoteId = 123 };
Quote q2 = new Quote { QuoteId = 345 };
repMock.Setup(m => m.GetQuotes(It.IsAny<Expression<Func<Quote, bool>>>())).Returns((new List<Quote> { q1, q2 }).AsQueryable);

这是一个 LINQ 语句,它将这些 Id 视为字符串,并获取包含字符串“3”(即两者)的那些:

Assert.AreEqual(2, repMock.Object.GetQuotes(q => q.QuoteId.ToString().Contains("3")).Count());

下面是用于仅获取一个字符串的相同原则 - 但是失败了。它 return 是两个字符串:

Assert.AreEqual(1, repMock.Object.GetQuotes(q => q.QuoteId.ToString().Contains("1")).Count());

然而,如果您将字符串拉出到它们自己的列表中并且 运行 包含反对,它会按预期工作:

List<string> foo = repMock.Object.GetQuotes(q => q.QuoteId.ToString().Contains("3")).Select(q => q.QuoteId.ToString()).ToList();
Assert.AreEqual(1, foo.Where(f => f.Contains("1")).Count());

这是怎么回事?这段代码似乎在生产中确实有效,并按预期过滤了字符串——它只在单元测试中失败了?

编辑:我看到@IvanStoev 在这里所说的逻辑,我将 Mock 设置为 return 两个对象的列表,而不考虑传入的函数。那么我怎样才能得到测试兑现功能?

我觉得问题出在这里

repMock.Setup(m => m.GetQuotes(It.IsAny<Expression<Func<Quote, bool>>>()))
    .Returns((new List<Quote> { q1, q2 }).AsQueryable);

我不是很熟悉模型框架,但从逻辑上看,您正在设置 GetQuotes 函数接收谓词始终 return 整个列表,忽略传递的谓词,所以这就是为什么你的测试代码总是 return 2 项。

更新:根据你的编辑,我想你可以使用这样的东西

repMock.Setup(m => m.GetQuotes(It.IsAny<Expression<Func<Quote, bool>>>()))
    .Returns((Expression<Func<Quote, bool>> predicate) => 
        (new List<Quote> { q1, q2 }).AsQueryable().Where(predicate));