NSubstitute - 测试特定的 linq 表达式 [英] NSubstitute - Testing for a specific linq expression

查看:22
本文介绍了NSubstitute - 测试特定的 linq 表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using the repository pattern in an MVC 3 application I am currently developing. My repository interface looks as follows:

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 允许您指定要在代码中测试的特定表达式.

In a lot of instances, when coding methods in my service classes, I am using the FindFirst and Find methods. As you can see, they both take a linq expression as input. What I am wanting to know is whether there is a way NSubstitute allows you to specify the particular expression you want to test for in your code.

所以,这里有一个服务方法的例子,它说明了我提到的其中一个 Repository 方法的使用:

So, here is an example of a service method that illustrates the use of one of the Repository methods I have mentioned:

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 ?

So, is there a way, using NSubtitute, that I can test for the specific lamda expression: i => !i.IsProcessed && i.IsConfirmed ?

任何指导将不胜感激.

推荐答案

非常简短的答案是否定的,NSubstitute 没有构建任何东西来简化特定表达式的测试.

The very short answer is no, NSubstitute doesn't have anything built it to make testing specific expressions easier.

更长的答案是,您可以尝试几个选项,其中大部分都涉及避免在被测类中直接使用 LINQ.我不确定这些是否是好主意,因为我不知道完整的上下文,但希望这里有一些信息可供您使用.在以下示例中,我取消了 Mapper 步骤以使代码示例更小.

The much longer answer is that there are a few options you can try, and most of them involve avoiding direct use of LINQ in the class under test. I'm not sure if any of these are good ideas as I don't know the full context, but hopefully there will be some info here you can use. In the following examples I've eliminated the Mapper step to make the code samples a bit smaller.

第一个选项是创建它,以便您可以检查表达式是否与您期望的引用相同,这意味着您不能再直接在被测代码中创建它.例如:

First option is to make it so you can check the expression is the same reference you are expecting, which means you can no longer create it directly in your code under test. For example:

//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 类上,但您可以使用工厂来更好地封装它.因为您有对所使用的实际表达式的引用,所以您可以设置返回值并检查是否正常接收到调用.您也可以单独测试表达式.

I've dumped the expression on a static Queries class, but you could use a factory to encapsulate it better. Because you have an reference to the actual expression used you can set return values and check calls were received as normal. You can also test the expression in isolation.

第二个选项通过使用规范模式更进一步.假设您将以下成员添加到 IRepository 接口并引入 ISpecification:

Second option takes this a bit further by using a specification pattern. Say you add the following member to the IRepository interface and introduce an ISpecification:

public interface IRepository<TEntity> where TEntity : IdEntity
{
   /* ...snip... */
    IList<TEntity> Find(ISpecification<TEntity> query);
}

public interface ISpecification<T> { bool Matches(T item);  }

然后你可以像这样测试它:

You can then test it like this:

//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));
}

同样,您可以单独测试此查询,以确保它符合您的想法.

Again, you can test this query in isolation to make sure it does what you think.

第三个选项是捕获使用的参数并直接测试它.这有点乱,但有效:

Third option is to catch the argument used and test it directly. This is a bit messy but works:

[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 版本中变得更容易)

(This will hopefully be getting a bit easier in future NSubstitute versions)

第四种选择是找到/借用/编写/窃取一些可以比较表达式树的代码,并使用 NSubstitute 的 Arg.Is(...) 来比较那里的表达式树.

Fourth option would be to find/borrow/write/steal some code that can compare expression trees, and use NSubstitute's Arg.Is(...) that takes a predicate to compare the expression trees there.

第五个选项是不对它进行那种程度的单元测试,而只是使用真正的 InvoiceRepository 进行集成测试.与其担心发生的事情的机制,不如尝试验证您需要的实际行为.

Fifth option is to not unit test it to that degree, and just integration test using a real InvoiceRepository. Rather than worry about the mechanics of what's happening, try verifying the actual behaviour you require.

我的一般建议是确切地查看您需要测试的内容以及如何最好且最轻松地编写这些测试.请记住,表达式和它通过的事实都需要以某种方式进行测试,并且测试不必是单元测试.也可能值得考虑当前的 IRepository 接口是否让您的生活更轻松.您可以尝试编写您喜欢的测试,然后看看您可以推出哪些设计来支持这种可测试性.

My general advice would be to look at exactly what you need to test and how you can best and most easily write those tests. Remember that both the expression and the fact that it is passed through needs to be tested somehow, and the test need not be a unit test. It may also be worth considering whether the current IRepository interface is making your life easier. You could try writing the tests you would like to have, then see what design you can drive out to support that testability.

希望这会有所帮助.

这篇关于NSubstitute - 测试特定的 linq 表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆