Search code examples
unit-testingmoqentity-framework-6.1asp.net-identity-2

Moq Mocking with Identity 2.0 Database


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?


Solution

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