Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# NSsubstitute-特定linq表达的测试_C#_Unit Testing_Nsubstitute - Fatal编程技术网

C# NSsubstitute-特定linq表达的测试

C# NSsubstitute-特定linq表达的测试,c#,unit-testing,nsubstitute,C#,Unit Testing,Nsubstitute,我正在开发的MVC3应用程序中使用存储库模式。我的存储库界面如下所示: public interface IRepository<TEntity> where TEntity : IdEntity { void Add(TEntity entity); void Update(TEntity entity); void Remove(TEntity entity); TEntity GetById(int id); IList<TEnti

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

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);
}
那么,有没有一种方法可以使用NSubtitute测试特定的lamda表达式:
I=>!i、 i已处理和i.i已确认


任何指导都将不胜感激。

非常简短的答案是否定的,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));
}
//测试中的类使用:
_invoiceRepository.Find(查询.未处理的确认者)
[测试]
public void testunprocessedvoices()
{
IList expectedResults=新列表();
_invoiceRepository.Find(查询.未处理的确认者).返回(expectedResults);
Assert.That(_sut.getUnprocessedVoices(),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);  }
公共接口i假设,其中tenty:IdEntity
{
/*…剪断*/
IList查找(指定查询);
}
公共接口指定{bool Matches(T项);}
然后您可以像这样测试它:

//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));
}
//正在测试的类现在使用:
_Find(新的未经处理的confirmedordersquery());
[测试]
public void TestUnprocessedVoiceSusingSpecification()
{
IList expectedResults=新列表();
_invoiceRepository.Find(Arg.Any()).Returns(expectedResults);
Assert.That(_sut.getUnprocessedVoices(),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 });
}
[测试]
public void testunprocessedVoicesByCatchingExpression()
{
表达式queryUsed=null;
IList expectedResults=新列表();
_发票存储库
.Find(i=>true)
.ReturnsForAnyArgs(x=>
{
queryUsed=(表达式)x[0];
返回预期结果;
});
Assert.That(_sut.getUnprocessedVoices(),Is.SameAs(expectedResults));
AssertQueryPasseFor(queryUsed,new InvoiceDTO{IsProcessed=false,IsConfirmed=true});
AssertQueryFailsFor(已查询,新发票到{IsProcessed=true,IsConfirmed=true});
}
(希望在将来的NSubstitute版本中这会变得更容易一些)

第四个选项是查找/借用/编写/窃取一些可以比较表达式树的代码,并使用NSubstitute的Arg.Is(…),它使用一个谓词来比较那里的表达式树

第五种选择是不进行单元测试,只使用真实的InvoiceRepository进行集成测试。与其担心正在发生的事情的机制,不如尝试验证您所需要的实际行为

我的一般建议是,准确地看一下您需要测试什么,以及如何最好、最容易地编写这些测试。请记住,表达式及其传递的事实都需要以某种方式进行测试,而测试不必是单元测试。也许还值得考虑当前的IRepository界面是否让您的生活更轻松。您可以尝试编写您想要的测试,然后看看您可以推出什么设计来支持这种可测试性


希望这能有所帮助。

当我试图找出如何使用NSubstitute中的lambda表达式返回特定值时,偶然发现了这个问题。然而,对于我的用例,我并不关心实际传递到linq查询中的内容,而是想分享如何在NSubstitute中为模拟接口上的linq查询返回值

所以使用上面的例子

[Test]
public void TestUnprocessedInvoices()
{
    IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>();
    _invoiceRepository.Find(Arg.Any<Expression<Func<Invoice, bool>>>()).Returns(expectedResults);
}
[测试]
public void testunprocessedvoices()
{
IList expectedResults=新列表();
_invoiceRepository.Find(Arg.Any()).Returns(expectedResults);
}

我不愿意放弃在我的存储库界面中使用
表达式
,作为编写这一特定模拟的替代方案(因为NSubstitute不支持它),我只是在我的测试夹具中创建了一个私有类,该类实现了我的存储库接口,并且只实现了测试将使用的与表达式相关的方法。我可以像往常一样继续使用NSubstitute来模拟所有其他依赖项,但我可以将同一个存储库用于几个不同的测试,并从不同的输入中获得不同的结果

public class SomeFixture
{
    private readonly IRepository<SomeEntity> entityRepository;
    private readonly IRepository<SomeThing> thingRepository;

    public SomeFixture()
    {
        var entities = new List<SomeEntity>
        {
            BuildEntityForThing(1),
            BuildEntityForThing(1),
            BuildEntityForThing(1),
            BuildEntityForThing(2),
        };
        entityRepository = new FakeRepository(entities);

        thingRepository = Substitute.For<IRepository<SomeThing>>();
        thingRepository.GetById(1).Returns(BuildThing(1));
        thingRepository.GetById(2).Returns(BuildThing(2));
    }

    public void SomeTest()
    {
        var classUnderTest = new SomeClass(thingRepository, entityRepository);

        Assert.AreEqual(classUnderTest.FetchEntitiesForThing(1).Count, 3);
    }

    private void SomeOtherTest()
    {
        var classUnderTest = new SomeClass(thingRepository, entityRepository);

        Assert.AreEqual(classUnderTest.FetchEntitiesForThing(2).Count, 1);
    }

    private class FakeRepository : IRepository<SomeEntity>
    {
        private readonly List<SomeEntity> items;

        public FakeRepository(List<SomeEntity> items)
        {
            this.items = items;
        }

        IList<TEntity> Find(Expression<Func<SomeEntity, bool>> criteria)
        {
            // For these purposes, ignore possible inconsistencies 
            // between Linq and SQL when executing expressions
            return items.Where(criteria.Compile()).ToList();
        }

        // Other unimplemented methods from IRepository ...
        void Add(SomeEntity entity)
        {
            throw new NotImplementedException();
        }
    }
}
公共类
{
私有只读存储实体存储;
私有只读IRepository thingRepository;
公共设施(
{
var实体=新列表
{
建筑业(1),
建筑业(1),
建筑业(1),
建筑业(2),
};
entityRepository=新伪造的(实体);
thingRepository=替换为();
GetById(1).Returns(BuildThing(1));
GetById(2).Returns(BuildThing(2));
}
公众的
public class SomeFixture
{
    private readonly IRepository<SomeEntity> entityRepository;
    private readonly IRepository<SomeThing> thingRepository;

    public SomeFixture()
    {
        var entities = new List<SomeEntity>
        {
            BuildEntityForThing(1),
            BuildEntityForThing(1),
            BuildEntityForThing(1),
            BuildEntityForThing(2),
        };
        entityRepository = new FakeRepository(entities);

        thingRepository = Substitute.For<IRepository<SomeThing>>();
        thingRepository.GetById(1).Returns(BuildThing(1));
        thingRepository.GetById(2).Returns(BuildThing(2));
    }

    public void SomeTest()
    {
        var classUnderTest = new SomeClass(thingRepository, entityRepository);

        Assert.AreEqual(classUnderTest.FetchEntitiesForThing(1).Count, 3);
    }

    private void SomeOtherTest()
    {
        var classUnderTest = new SomeClass(thingRepository, entityRepository);

        Assert.AreEqual(classUnderTest.FetchEntitiesForThing(2).Count, 1);
    }

    private class FakeRepository : IRepository<SomeEntity>
    {
        private readonly List<SomeEntity> items;

        public FakeRepository(List<SomeEntity> items)
        {
            this.items = items;
        }

        IList<TEntity> Find(Expression<Func<SomeEntity, bool>> criteria)
        {
            // For these purposes, ignore possible inconsistencies 
            // between Linq and SQL when executing expressions
            return items.Where(criteria.Compile()).ToList();
        }

        // Other unimplemented methods from IRepository ...
        void Add(SomeEntity entity)
        {
            throw new NotImplementedException();
        }
    }
}
var mockRepository = Substitute.For<IRepository>();
mockRepository.Find(Arg.Is<Expression<Func<Invoice, bool>>>(expr =>
                    LambdaCompare.Eq(expr, i => !i.IsProcessed && i.IsConfirmed))
              .Returns(..etc..)