4

我在 MVC 5、Entity Framework 6 应用程序中成功地使用 Moq 模拟对我的 BusAct 控制器进行了集成测试,该应用程序基于一个简单的成员资格数据库。但现在我已将数据库迁移到 Identity 2.0 并将 UserProfile 替换为 ApplicationUser。

IdentityDbContext:(修改自简单成员 DbContext)

  public class MyDb : IdentityDbContext<ApplicationUser> // DbContext  
  {
    public MyDb () : base("MyApplication") { }

    // public virtual DbSet<UserProfile> UserProfiles { get; set; }
    public virtual DbSet<BusAcnt> BusAcnts { get; set; } // marking as virtual allows mocking override
    public virtual DbSet<...>
    ...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
      base.OnModelCreating(modelBuilder);
      modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
  }

模拟数据库设置:

  public class MockDbSetup
  {
    public static Mock<MyDb> MockMyDb()
    {
      var dataBa = new List<BusAcnt>  {
        new BusAcnt {Id = 0, CmpnyName = "Company 1", NmOfc = 1, Status = "Active"},
        new BusAcnt {Id = 1, CmpnyName = "Company 2", NmOfc = 1, Status = "Active"},
        new BusAcnt {Id = 2, CmpnyName = "Company 3", NmOfc = 1, Status = "Active"},
        new BusAcnt {Id = 3, CmpnyName = "Company 4", NmOfc = 1, Status = "Active"},
        new BusAcnt {Id = 4, CmpnyName = "Company 5", NmOfc = 1, Status = "Active"},
        new BusAcnt {Id = 5, CmpnyName = "Company 6", NmOfc = 1, Status = "Active"}
      }.AsQueryable();
      var mockSetBa = new Mock<DbSet<BusAcnt>>();
      mockSetBa.As<IQueryable<BusAcnt>>().Setup(m => m.Provider).Returns(dataBa.Provider);
      mockSetBa.As<IQueryable<BusAcnt>>().Setup(m => m.Expression).Returns(dataBa.Expression);
      mockSetBa.As<IQueryable<BusAcnt>>().Setup(m => m.ElementType).Returns(dataBa.ElementType);
      mockSetBa.As<IQueryable<BusAcnt>>().Setup(m => m.GetEnumerator()).Returns(dataBa.GetEnumerator());
      var MyDb = new Mock<MyDb>();
      mockMyDb.Setup(c => c.BusAcnts).Returns(mockSetBa.Object);
      return mockMyDb;
    }
  }

集成测试:

[Fact]
public void GetAllBusAcnt()
{
  var mockMyDb = MockDBSetup.MockMyDb();
  var controller = new BusAcntController(mockMyDb.Object);
  var controllerContextMock = new Mock<ControllerContext>();
  controllerContextMock.Setup(
      x => x.HttpContext.User.IsInRole(It.Is<string>(s => s.Equals("admin")))
      ).Returns(true);
  controller.ControllerContext = controllerContextMock.Object;

  var viewResult = controller.Index() as ViewResult;
  var model = viewResult.Model as PagedBusIdxModel;

  Assert.NotNull(model);
  Assert.Equal(6, model.BusAcnts.ToList().Count());
  Assert.Equal("Company 2", model.BusAcnts.ToList()[1].CmpnyName);
}

当我现在运行测试时,出现以下错误:

System.Data.Entity.ModelConfiguration.ModelValidationExceptionOne or more validation errors were detected during model generation:

Castle.Proxies.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
Castle.Proxies.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
   at System.Data.Entity.Core.Metadata.Edm.EdmModel.Validate()
   at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
   at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
   at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
   at System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.InternalContext.CreateObjectContextForDdlOps()
   at System.Data.Entity.Database.Exists()
   at Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext`1.IsIdentityV1Schema(DbContext db)
   at Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext`1..ctor(String nameOrConnectionString, Boolean throwIfV1Schema)
   at Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext`1..ctor(String nameOrConnectionString)
   at MyApplication.Models.MyDb..ctor() in MyDb.cs: line 9
   at Castle.Proxies.MyDbProxy..ctor(IInterceptor[])

我相信我需要修改 MockDbSetup 但无法找到有关如何执行此操作的任何信息。

我尝试添加

  var dataUsr = new List<ApplicationUser>
  {
    new ApplicationUser { UserName = "Test", PasswordHash = "a123cdefg"} }.AsQueryable();
  var mockSetUsr = new Mock<DbSet<ApplicationUser>>();
  mockSetUsr.As<IQueryable<BusAcnt>>().Setup(m => m.Provider).Returns(dataUsr.Provider);
  mockSetUsr.As<IQueryable<BusAcnt>>().Setup(m => m.Expression).Returns(dataUsr.Expression);
  mockSetUsr.As<IQueryable<BusAcnt>>().Setup(m => m.ElementType).Returns(dataUsr.ElementType);
  mockSetUsr.As<IQueryable<BusAcnt>>().Setup(m => m.GetEnumerator()).Returns(dataUsr.GetEnumerator()); 

但我收到“无法解决”错误GetEnumerator

也没有

  mockMyDb.Setup(c => c.AspNetUsers).Returns(mockSetUsr.Object);  

或者

  mockMyDb.Setup(c => c.ApplicationUser).Returns(mockSetUsr.Object);

有效,因为它无法解决AspNetUsersor ApplicationUser

有没有人知道如何做到这一点,关于模拟 Identity 2.0 数据库的文档的链接会特别有用吗?

4

2 回答 2

3

感谢 Jamie,而 Andreas 在测试框架中的回答说实体没有为内置实体定义键。

MockMyDb()方法中MockDbSetup添加{ CallBase = true };var MyDb = new Mock<MyDb>();导致

var MyDb = new Mock<MyDb>() { CallBase = true };
于 2014-05-20T14:49:07.273 回答
2

虽然可能有一种方法可以让 EF 绕过其内部行为以进行模拟,但到目前为止,最简单的方法是使用 IDbSet 而不是 DbSet,并从 MyDb 实例中提取接口。

public interface IMyDb   
{
    IDbSet<BusAcnt> BusAcnts { get; set; } // marking as virtual allows mocking override
    IDbSet<...>

    int SaveChanges();
}

您目前正在做的是部分模拟,我一直认为这是有问题的。我只是避免它。

于 2014-05-08T23:12:00.777 回答