Moq Mocking with Identity 2.0 Database

给你一囗甜甜゛ 提交于 2020-01-01 12:06:09

问题


I successfully set up an Integration Test using mocking with Moq against my BusAct controller in an MVC 5, Entity Framework 6 app based on a Simple membership database. But now I have migrated the database to Identity 2.0 and replaced the UserProfile with ApplicationUser.

IdentityDbContext: (Modified from Simple membership 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>();
    }
  }

MockDbSetup:

  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;
    }
  }

Integration Test:

[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);
}

When I run the test now I get these errors:

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[])

I believe I need to modify the MockDbSetup but have not been able to find any information on how to do that.

I tried adding

  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()); 

But I get a "Can't resolve" error on GetEnumerator

And neither

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

or

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

works because it cannot resolve either AspNetUsers or ApplicationUser.

Does anyone have an idea of how to do this, links to documentation on mocking an Identity 2.0 database would be especially helpful?


回答1:


Thank you to Jamie and in turn Andreas' answer at Testing framework says entity has no key defined for built in entity.

In the MockMyDb() method in MockDbSetup add { CallBase = true }; to var MyDb = new Mock<MyDb>(); resulting in

var MyDb = new Mock<MyDb>() { CallBase = true };



回答2:


While there may be a way to get EF to bypass it's internal behavior for the purpose of mocking, by far the easiest method is to use IDbSet instead of DbSet, and extract an interface off of your MyDb instance.

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

    int SaveChanges();
}

What you're currently doing is a partial mock which I've always found to be problematic. I just avoid it.



来源:https://stackoverflow.com/questions/23484368/moq-mocking-with-identity-2-0-database

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!