非常简短的回答是否定的,NSubstitute 没有构建任何东西来简化特定表达式的测试。
更长的答案是您可以尝试一些选项,其中大多数涉及避免在被测类中直接使用 LINQ。我不确定这些是否是好主意,因为我不知道完整的上下文,但希望您可以在这里使用一些信息。在下面的示例中,我消除了 Mapper 步骤以使代码示例更小一些。
第一个选项是创建它,以便您可以检查表达式是否与您期望的引用相同,这意味着您不能再直接在被测代码中创建它。例如:
//Class under test uses:
_invoiceRepository.Find(Queries.UnprocessedConfirmedOrders)
[Test]
public void TestUnprocessedInvoices()
{
IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>();
_invoiceRepository.Find(Queries.UnprocessedConfirmedOrders).Returns(expectedResults);
Assert.That(_sut.GetUnprocessedInvoices(), Is.SameAs(expectedResults));
}
我已将表达式转储到静态查询类上,但您可以使用工厂更好地封装它。因为您引用了实际使用的表达式,所以您可以设置返回值并检查是否正常接收调用。您还可以单独测试表达式。
第二个选项通过使用规范模式更进一步。假设您将以下成员添加到 IRepository 接口并引入 ISpecification:
public interface IRepository<TEntity> where TEntity : IdEntity
{
/* ...snip... */
IList<TEntity> Find(ISpecification<TEntity> query);
}
public interface ISpecification<T> { bool Matches(T item); }
然后你可以像这样测试它:
//Class under test now uses:
_invoiceRepository.Find(new UnprocessedConfirmedOrdersQuery());
[Test]
public void TestUnprocessedInvoicesUsingSpecification()
{
IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>();
_invoiceRepository.Find(Arg.Any<UnprocessedConfirmedOrdersQuery>()).Returns(expectedResults);
Assert.That(_sut.GetUnprocessedInvoices(), Is.SameAs(expectedResults));
}
同样,您可以单独测试此查询,以确保它符合您的想法。
第三种选择是捕获使用的参数并直接测试它。这有点混乱但有效:
[Test]
public void TestUnprocessedInvoicesByCatchingExpression()
{
Expression<Func<InvoiceDTO, bool>> queryUsed = null;
IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>();
_invoiceRepository
.Find(i => true)
.ReturnsForAnyArgs(x =>
{
queryUsed = (Expression<Func<InvoiceDTO, bool>>)x[0];
return expectedResults;
});
Assert.That(_sut.GetUnprocessedInvoices(), Is.SameAs(expectedResults));
AssertQueryPassesFor(queryUsed, new InvoiceDTO { IsProcessed = false, IsConfirmed = true });
AssertQueryFailsFor(queryUsed, new InvoiceDTO { IsProcessed = true, IsConfirmed = true });
}
(这有望在未来的 NSubstitute 版本中变得更容易一些)
第四种选择是查找/借用/编写/窃取一些可以比较表达式树的代码,并使用 NSubstitute 的 Arg.Is(...) 接受谓词来比较那里的表达式树。
第五个选项是不对它进行那种程度的单元测试,而只是使用真正的 InvoiceRepository 进行集成测试。与其担心正在发生的事情的机制,不如尝试验证您需要的实际行为。
我的一般建议是准确查看您需要测试的内容以及如何最好、最轻松地编写这些测试。请记住,表达式和它通过的事实都需要以某种方式进行测试,并且测试不必是单元测试。可能还值得考虑当前的 IRepository 接口是否让您的生活更轻松。你可以尝试编写你想要的测试,然后看看你可以推出什么样的设计来支持这种可测试性。
希望这可以帮助。