NSubstitute-测试特定的linq表达式 - c#

我在当前正在开发的MVC 3应用程序中使用存储库模式。我的存储库界面如下所示:

public interface IRepository<TEntity> where TEntity : IdEntity
{
    void Add(TEntity entity);
    void Update(TEntity entity);
    void Remove(TEntity entity);
    TEntity GetById(int id);
    IList<TEntity> GetAll();
    TEntity FindFirst(Expression<Func<TEntity, bool>> criteria);
    IList<TEntity> Find(Expression<Func<TEntity, bool>> criteria);
}

在很多情况下,在服务类中对方法进行编码时,我使用的是FindFirstFind方法。如您所见,它们都使用linq表达式作为输入。我想知道的是NSubstitute是否允许您在代码中指定要测试的特定表达式。

因此,这是一个服务方法的示例,该示例说明了我提到的存储库方法之一的用法:

public IList<InvoiceDTO> GetUnprocessedInvoices()
{
    try
    {
        var invoices = _invoiceRepository.Find(i => !i.IsProcessed && i.IsConfirmed);
        var dtoInvoices = Mapper.Map<IList<Invoice>, IList<InvoiceDTO>>(invoices);
        return dtoInvoices;
    }
    catch (Exception ex)
    {
        throw new Exception(string.Format("Failed to get unprocessed invoices: {0}", ex.Message), ex);
    }
}

因此,有没有一种方法可以使用NSubtitute测试特定的lamda表达式:i => !i.IsProcessed && i.IsConfirmed

任何指导将不胜感激。

参考方案

简短的回答是“否”,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));
}

我已将表达式转储到静态Queries类中,但可以使用工厂更好地对其进行封装。因为您有对所使用的实际表达式的引用,所以可以设置返回值并检查是否正常接收到调用。您也可以单独测试表达式。

第二种选择是通过使用规范模式使这一点更进一步。假设您将以下成员添加到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接口是否使您的生活更轻松。您可以尝试编写所需的测试,然后查看可以采用哪些设计来支持该可测试性。

希望这可以帮助。

将谓词<T>转换为Func <T,bool> - c#

我有一个包含成员Predicate的类,希望在Linq表达式中使用该类:using System.Linq; class MyClass { public bool DoAllHaveSomeProperty() { return m_instrumentList.All(m_filterExpression); } private IEnumerable&…

合并List <T>和List <Optional <T >> - java

鉴于: List<Integer> integers = new ArrayList<>(Arrays.asList( 10, 12 )); List<Optional<Integer>> optionalIntegers = Arrays.asList( Optional.of(5), Optional.em…

根据激活的Maven配置文件更新战争名称 - java

在pom中,我有两个配置文件。测试1测试2现在,我希望根据激活的配置文件更改战争名称。预期结果激活test1配置文件后,战争名称应为prefix-test1.war。激活test1和test2时,战争名称应为prefix-test1-test2.war。如果没有激活任何配置文件,则战争名称应为prefix.war。我的POM文件....<?xml ve…

无法从ArrayList <String>转换为List <Comparable> - java

当我写下面的代码时,编译器说 无法从ArrayList<String>转换为List<Comparable>private List<Comparable> get(){ return new ArrayList<String>(); } 但是当我用通配符编写返回类型时,代码会编译。private List&l…

将对象转换为List <object> - c#

我看过类似的问题,但没有什么合适的。我有一个碰巧包含列表的对象。我想把它变成我可以列举的东西。例如:object listObject; // contains a List<Something> List<object> list; list = listObject as List<object>; // list c…