c# - 在 UnitTest Mock-DbContext 上使用

标签 c# asp.net-mvc entity-framework unit-testing mocking

在我的 C# 项目中,我有一个末尾带有 .ToList(); 的查询:

public List<Product> ListAllProducts()
{
    List<Product> products = db.Products
        .Where(...)
        .Select(p => new Product()
        {
            ProductId = p.ProductId,
            ...
        })
        .ToList();

    return products;
}

时不时地,我会收到EntityException:“底层提供程序在打开时失败。” 错误。我根据我在 SO 上找到的答案做了以下解决方法

public List<Product> ListAllProducts()
{
    try
    {
        // When accessing a lot of Database Rows the following error might occur:
        // EntityException: "The underlying provider failed on Open." With an inner TimeoutExpiredException.
        // So, we use a new db so it's less likely to occur
        using(MyDbContext usingDb = new MyDbContext())
        {
            // and also set the CommandTimeout to 2 min, instead of the default 30 sec, just in case
            ((IObjectContextAdapter)usingDb).ObjectContext.CommandTimeout = 120;

            List<Product> products = usingDb.Products
                .Where(...)
                .Select(p => new Product()
                {
                    ProductId = p.ProductId,
                    ...
                })
                .ToList();

            return products;
        }
    }
    // and also have a try-catch in case the error stil occurres,
    // since we don't want the entire website to crash
    catch (EntityException ex)
    {
        Console.WriteLine("An error has occured: " + ex.StackTrace);
        return new List<Product>();
    }
}

到目前为止,它似乎有效,我每次都会收到数据(以防万一我还添加了 try-catch)。

现在我正在进行单元测试,但遇到了一个问题。我的 UnitTest 中有一个 MockDbContext ,但由于我使用的是非 Mock 版本的 using(...) ,所以我的 UnitTest 失败了。我如何使用 MockDbContext 而不是默认的来测试这一点?


编辑:

我刚刚遇到this post about UnitTesting Mock DbContext 。所以在我看来,我确实需要将它一直传递到我的方法,或者只是不使用 using(MyDbContext usingDb = new MyDbContext()) 而只是暂时增加 CommandTimeout并使用try-catch。真的没有别的办法了吗?或者,对于我遇到的错误,是否有更好的修复/解决方法,而无需使用 using(MyDbContext usingDb = new MyDbContext())


编辑2:

为了让我的情况更清楚:

我有 MyDbContext 类和 IMyDbContext 接口(interface)。

我的每个 Controller 中都有以下内容:

public class ProductController : Controller
{
    private IMyDbContext _db;

    public ProductController(IMyDbContext db)
    {
        this._db = db;
    }

    ... // Methods that use this "_db"

    public List<Product> ListAllProducts()
    {
        try
        {
            using(MyDbContext usingDb = new MyDbContext())
            {
                ... // Query that uses "usingDb"
            }
        }
        catch(...)
        {
            ...
        }
    }
}

在我的正常代码中,我创建如下实例:

ProductController controller = new ProductController(new MyDbContext());

在我的 UnitTest 代码中,我创建了如下实例:

ProductController controller = new ProductController(this.GetTestDb());

// With GetTestDb something like this:
private IMyDbContext GetTestDb()
{
    var memoryProducts = new Product { ... };
    ...

    var mockDB = new Mock<FakeMyDbContext>();
    mockDb.Setup(m => m.Products).Returns(memoryProducts);
    ...
    return mockDB.Object;
}

除了 ListAll 方法中的 using(MyDbContext usingDb = new MyDbContext()) 部分之外,在我作为 UnitTest 的正常项目中,一切都运行良好。

最佳答案

虽然希望分解代码以促进测试,但仅在测试中执行的代码分支并没有帮助。实际上,它的作用是将测试环境配置代码放入您的实时代码中。

您需要的是创建 DbContext 的更多抽象。您应该传入一个构造DbContext的委托(delegate):

public List<Product> ListAllProducts(Func<DbContext> dbFunc)
{
    using(MyDbContext usingDb = dbFunc())
    {
        // ...
    }
}

现在您不再传递繁重的 DbContext 对象,您可以将方法参数重构到类构造函数中。

public class ProductDAL
{
    private Func<DbContext> _dbFunc;

    public ProductDAL(Func<DbContext> dbFunc)
    {
        _dbFunc = dbFunc;
    }

    public List<Product> ListAllProducts()
    {
        using(MyDbContext usingDb = _dbFunc())
        {
            // ...
        }
    }
}

最重要的是,您现在可以控制要使用的 DbContext,包括从配置文件中提取的连接字符串,一直在 IOC 容器中,而无需在任何地方担心它代码中的其他内容。

关于c# - 在 UnitTest Mock-DbContext 上使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25061160/

相关文章:

c# - C# 中的事件、操作 - 在线程上下文中

c# - 如果是 WebAPI 中的 int,则覆盖消息 “the value {0} is invalid for {1}”

c# - MVC 单字符串 JSON 帖子在 Controller 中始终为 Null

c# - 无法使用独立关联将可选外键设置为空

c# - 在 Entity Framework 中使用 AddRange 批量插入

c# - ASP.Net MVC 3 与 Oracle

c# - 代码在本地 Windows 2008 上运行正常,但无法在 Azure 云上的应用程序数据中创建子文件夹

Asp.net web api + Entity Framework : multiple requests cause data conflict

c# - 将多个来源合并到一个目的地

entity-framework - 为 EntitySet 生成的查询 View 无效