DbContext.ChangeTracker in automated tests throws SQLException
我正在为一个我一直在努力的项目编写自动化测试,以便更熟悉MVC、EntityFramework(代码优先)、单元测试和MOQ。
我在我的
1 2 3 4 5 6 7 8 9 10 11 12 | 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并设置测试数据。
这是我奇怪的地方:我的单元测试运行良好(通过),但它们从未真正进入过
但是,在一个与我共享项目的朋友的机器上,当访问
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(我怀疑不是从先前的调查中得出的),或者有没有另一种方法可以使用我的
对于我的机器上为什么没有抛出/不能捕获sqlException,有人有什么见解吗?或者对如何在单元测试中使用
更新:已经请求了更多的示例代码,所以让我进一步了解细节。我使用moq模拟dbContext,然后模拟dbContext中包含的dbset对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 |
我得出了一个不太理想的解决方案,但足以让我继续前进。我绕过了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; } } } } } |
这样,我仍然可以通过调用
1 2 3 | List<SomeModel> addedEntries = new List<SomeModel>(); addedEntries.add(someModelWhichWillBeAddedByTheController); mockContext.Setup(context => context.AddedEntries).Returns(addedEntries); |
这样,当使用EDCOX1×19属性时,控制器将观察EDCOX1×18Ω。缺点是,我无法测试实际业务逻辑中使用的EDCOX1 12码,但稍后我将通过实现与测试数据库的集成测试来实现这一点。
我一直找不到EDCOX1 21被抛出一台机器而不是另一台机器的原因。
例外情况是它无法创建到数据库的连接,我怀疑你和你的朋友在app.config文件中有不同的连接字符串,或者你的朋友没有访问数据库的权限。
实际上,您正在编写一个集成测试,而不是单元测试。单元测试是专门为测试对象编写的。在您的案例代码中:
1 2 | PersonController controller = new PersonController(); var result = controller.Create(someEmployerID); |
不应使用实际的EDOCX1实现〔0〕。您需要将存储库的模拟实例注入到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private IRepository _repository; public PersonController() : this(new Repository()) //real implementation {} public PersonController(IRepository repository) { _repository = repository; } // your test var reporitory = new Mock<IRepository>(); var controller = new PersonController(repository.Object); controller.CreateEmployee(someId); // assert that your repository was called repository.Verify(...); |
这项技术被称为穷人的注射,它不是推荐的注射方式,但它比只有具体的实例更好。
接下来,如果您想为您的
更新:忽略我上次关于
您的错误是它无法创建到数据库的连接。这是在创建dbContext时发生的,在该上下文中,ef将开始执行诸如检查是否需要迁移数据库之类的操作。
在我看来,您还需要模拟dbContext的构造函数。我不太熟悉moq本身,但是如果我正确地阅读了您的代码,我看不到您在嘲笑构造函数。