EntityFunctions.TruncateTime和单元测试 [英] EntityFunctions.TruncateTime and unit tests

查看:284
本文介绍了EntityFunctions.TruncateTime和单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 System.Data.Objects.EntityFunctions.TruncateTime 方法在我的查询中获取datetime的日期部分:

I'm using System.Data.Objects.EntityFunctions.TruncateTime method to get date part of a datetime in my query:

if (searchOptions.Date.HasValue)
    query = query.Where(c => 
        EntityFunctions.TruncateTime(c.Date) == searchOptions.Date);

此方法(我认为这同样适用于其他 EntityFunctions 方法)不能在LINQ to Entities之外执行。在单元测试中执行此代码,这有效地是LINQ对象,导致抛出 NotSupportedException

This method (I believe the same applies to other EntityFunctions methods) cannot be executed outside of LINQ to Entities. Executing this code in a unit test, which effectively is LINQ to objects, causes a NotSupportedException to be thrown:


System.NotSupportedException:此函数只能从
LINQ to Entities调用。

System.NotSupportedException : This function can only be invoked from LINQ to Entities.

I在我的测试中,我使用了一个虚假的 DbSets 的存根库。

I'm using a stub for a repository with fake DbSets in my tests.

那么我该怎么办单位测试我的查询?

推荐答案

您不能 - 如果单元测试意味着您正在使用假存储库在内存中,因此使用LINQ to Objects。如果您使用LINQ to Objects测试您的查询,您没有测试您的应用程序,但只测试了您的虚拟存储库。

You can't - if unit testing means that you are using a fake repository in memory and you are therefore using LINQ to Objects. If you test your queries with LINQ to Objects you didn't test your application but only your fake repository.

您的例外情况是不太危险的情况,因为它表示您有一个红色的测试,但可能实际上是一个工作的应用程序。

Your exception is the less dangerous case as it indicates that you have a red test, but probably actually a working application.

更危险的是另一种方式:进行绿色测试,但崩溃的应用程序或不返回的查询与您的测试相同的结果。查询如...

More dangerous is the case the other way around: Having a green test but a crashing application or queries which do not return the same results as your test. Queries like...

context.MyEntities.Where(e => MyBoolFunction(e)).ToList()

context.MyEntities.Select(e => new MyEntity { Name = e.Name }).ToList()

...在您的测试中可以正常工作,但不适用于您的应用程序中的LINQ to Entities。

...will work fine in your test but not with LINQ to Entities in your application.

像...一样的查询

context.MyEntities.Where(e => e.Name == "abc").ToList()

...可能会使用LINQ to Object比LINQ to Entities返回不同的结果。

...will potentially return different results with LINQ to Objects than LINQ to Entities.

您只能通过构建使用您的应用程序的LINQ to Entities提供程序和真实数据库的集成测试来测试此问题和查询。

You can only test this and the query in your question by building integration tests which are using the LINQ to Entities provider of your application and a real database.

编辑

如果您还想编写单元测试,我认为您必须在查询中伪造查询本身或至少表达式。我可以想象,符合以下代码的东西可能会起作用:

If you still want to write unit tests I think you must fake the query itself or at least expressions in the query. I could imagine that something along the lines of the following code might work:

创建一个接口,其中表达式:

public interface IEntityExpressions
{
    Expression<Func<MyEntity, bool>> GetSearchByDateExpression(DateTime date);
    // maybe more expressions which use EntityFunctions or SqlFunctions
}

创建一个应用程序的实现...

Create an implementation for your application...

public class EntityExpressions : IEntityExpressions
{
    public Expression<Func<MyEntity, bool>>
        GetSearchByDateExpression(DateTime date)
    {
       return e => EntityFunctions.TruncateTime(e.Date) == date;
       // Expression for LINQ to Entities, does not work with LINQ to Objects
    }
}

...您的单元测试项目中的第二个实现:

...and a second implementation in your Unit test project:

public class FakeEntityExpressions : IEntityExpressions
{
    public Expression<Func<MyEntity, bool>>
        GetSearchByDateExpression(DateTime date)
    {
        return e => e.Date.Date == date;
       // Expression for LINQ to Objects, does not work with LINQ to Entities
    }
}

在您使用查询的类中,创建此接口的私有成员和两个构造函数:

In your class where you are using the query create a private member of this interface and two constructors:

public class MyClass
{
    private readonly IEntityExpressions _entityExpressions;

    public MyClass()
    {
        _entityExpressions = new EntityExpressions(); // "poor man's IOC"
    }

    public MyClass(IEntityExpressions entityExpressions)
    {
        _entityExpressions = entityExpressions;
    }

    // just an example, I don't know how exactly the context of your query is
    public IQueryable<MyEntity> BuildQuery(IQueryable<MyEntity> query,
        SearchOptions searchOptions)
    {
        if (searchOptions.Date.HasValue)
            query = query.Where(_entityExpressions.GetSearchByDateExpression(
                searchOptions.Date));
        return query;
    }
}

使用应用程序中的第一个(默认)构造函数:

Use the first (default) constructor in your application:

var myClass = new MyClass();
var searchOptions = new SearchOptions { Date = DateTime.Now.Date };

var query = myClass.BuildQuery(context.MyEntities, searchOptions);

var result = query.ToList(); // this is LINQ to Entities, queries database

使用第二个构造函数与单位测试中的FakeEntityExpressions

Use the second constructor with FakeEntityExpressions in your unit test:

IEntityExpressions entityExpressions = new FakeEntityExpressions();
var myClass = new MyClass(entityExpressions);
var searchOptions = new SearchOptions { Date = DateTime.Now.Date };
var fakeList = new List<MyEntity> { new MyEntity { ... }, ... };

var query = myClass.BuildQuery(fakeList.AsQueryable(), searchOptions);

var result = query.ToList(); // this is LINQ to Objects, queries in memory

如果你使用依赖注入容器,你可以通过将 IEntityExpressions 注入到构造函数中,并且不需要默认构造函数来充分利用它。

If you are using a dependency injection container you could leverage it by injecting the appropriate implementation if IEntityExpressions into the constructor and don't need the default constructor.

I已经测试了上面的示例代码,它的工作。

I've tested the example code above and it worked.

这篇关于EntityFunctions.TruncateTime和单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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