c# - 自动化测试中的 DbContext.ChangeTracker 抛出 SQLException

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

我正在为我一直致力于的项目编写自动化测试,以更加熟悉 MVC、EntityFramework(代码优先)、单元测试和 Moq。

我的 Repository 类中有一个部分,每当 Repository.SaveChanges() 被像这样工作的 Controller (MyModelBase 是一个基类):

public void RepoSaveChanges()
{
    foreach(var entry in _dbContext.ChangeTracker.Entities().Where(e => e.State == EntityState.Modified))
    {
        MyModelBase model = entry.Entity as MyModelBase;
        if (model != null)
        {
            model.LastModified = DateTime.Now;
        }
    }
    _dbContext.SaveChanges();
}

这在正常在线环境中的应用程序运行时工作正常,但在测试方法中运行时会中断。我使用 Moq 在 DbContext 中模拟 DbSet 并设置我的测试数据。

这对我来说是奇怪的部分: 我的单元测试运行良好(通过)但它们从未真正进入 foreach 循环 - 当 ChangeTracker.Entities() 被访问并退出循环时它挂起,跳转到 _dbContext.SaveChanges()。没有错误。

但是,在一个与我共享项目的 friend 的机器上,当访问 ChangeTracker.Entities() 时,他得到了一个 SQLException。我确实检查了 VS2015 中抛出的 SQLExceptions,但我这边没有输出或其他异常指示。

Result StackTrace:
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) ....

Result Message:
Test method MyProject.Tests.TestClasses.MyControllerTests.TestCreate threw exception: System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)

最后,我的问题是:是否有一种方法可以使用 Moq 来模拟 ChangeTracker(我怀疑不是来自之前的调查),或者是否有另一种方法可以用于我的 RepoSaveChanges()自动设置一个属性?如果不访问 ChangeTracker.Entities(),我将需要更新逻辑来为每个具有它的模型类型设置 LastModified 字段。同样,我想避免使用该 API/框架的一部分,因为顽固的测试并不理想。

有人知道为什么我的机器上没有抛出/无法捕获 SQLException 吗?或者关于如何在单元测试中使用 ChangeTracker.Entities() 的任何建议?作为最后的手段,我只会在所有模型和 Controller 中单独设置 LastModified 属性。

更新: 已请求更多示例代码,所以让我进一步详细说明。我使用最小起订量模拟 DbContext,然后模拟 DbContext 中包含的 DbSet 对象:

var mockContext = new Mock<MyApplicationDbContext>();   //MyApplicationDbContext extends DbContext

Person p = new Person();
p.Name = "Bob";
p.Employer = "Superstore";

List<Person> list = new List<Person>();
list.Add(p);

var queryable = list.AsQueryable();

Mock<DbSet<Person>> mockPersonSet = new Mock<DbSet<Person>>();
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.Provider).Returns(queryable.Provider);
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.Expression).Returns(queryable.Expression);
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.ElementType).Returns(queryable.ElementType);
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.GetEnumerator()).Returns(() => queryable.GetEnumerator()); 

DbSet<Person> dbSet = mockPersonSet.Object as DbSet<Person>;
mockPersonSet.Setup(set => set.Local).Returns(dbSet.Local);

mockContext.Setup(context => context.Set<Person>()).Returns(mockPersonSet.Object);
mockContext.Setup(context => context.Persons).Returns(mockPersonSet.Object));

//Create the repo using the mock data context I created
PersonRepository repo = new PersonRepository(mockContext.Object);

//Then finally create the controller and perform the test
PersonController controller = new PersonController(repo);
var result = controller.Create(someEmployerID); //Sometimes throws an SQLException when DbContext.SaveChanges() is called

最佳答案

我为自己找到了一个不太理想的解决方案,但足以让我继续前进。我绕过了 DbContext.ChangeTracker.Entries() API,只是向我的类添加了一个抽象级别,该类扩展了名为 MyApplicationDbContextDbContext:

public class MyApplicationDbContext : IdentityDbContext<MyApplicationUser>
{
    //DbSets etc

    public virtual IEnumerable<MyModelBase> AddedEntries
    {
        get
        {               
            foreach (var entry in ChangeTracker.Entries().Where(entry => entry.State == EntityState.Added))
            {
                MyModelBase model = entry.Entity as MyModelBase;
                if (model != null)
                {
                    yield return model;
                }
            }
        }
    }
}

这样我仍然可以通过调用 MyApplicationDbContext.AddedEntries 而不是 MyApplicationDbContext.ChangeTracker.Entries() 来迭代问题陈述中描述的业务逻辑的 Entries() >。但是,由于我将属性设置为 virtual,因此我可以使用 Moq 设置返回:

List<SomeModel> addedEntries = new List<SomeModel>();
addedEntries.add(someModelWhichWillBeAddedByTheController);
mockContext.Setup(context => context.AddedEntries).Returns(addedEntries);

这样,当使用 AddedEntries 属性时, Controller 将观察到 someModelWhichWillBeAddedByTheController。不利的一面是我无法测试实际业务逻辑中使用的 DbContext.ChangeTracker.Entries() 代码,但我稍后可以通过使用测试数据库实现集成测试来实现这一点。

我一直找不到在一台机器上抛出 SQLException 而在另一台机器上抛出的原因。

关于c# - 自动化测试中的 DbContext.ChangeTracker 抛出 SQLException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33941445/

相关文章:

c# - 提高在 EF 中为一对多关系插入数据的性能

c# - 创建可在 LINQ (EF) 中使用的辅助方法

asp.net - asp :datalist in asp.net-mvc 相当于什么

c# - Mvc 4 添加带有脚手架的 Controller 给出错误 - "Unable to retrieve metadata..."

c# - 为什么我选择的值在下拉列表中显示两次 (asp.net mvc)

.net - 未找到 Sql.MigrationSqlGenerator.set_ProviderManifest

c# - 列出未链接到给定父级的子记录

c# - 使用 Entity Framework 从表中选择所有字段

C# - 数据库连接列表字符串提供程序

c# - Web应用中如何获取Web请求域用户信息