Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test what is setup in overridden OnModelCreating

I'm using Entity Framework 6 and in my context I have overridden OnModelCreating method:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

Inside the method, I set up the database configuration. I would like to test what is set up. For instance, I have the following code:

modelBuilder.HasDefaultSchema("public");

I would like to have a unit test which checks that HasDefaultSchema with parameter value "public" is called.

Or like in the following example I would like to test that HasMany and WithMany methods of entity UserGroup are called:

 modelBuilder.Entity<UserGroup>()
             .HasMany<User>(g => g.Users)
             .WithMany(u => u.Groups)
             .Map(ug =>
                  {
                     ug.MapLeftKey("GroupId");
                     ug.MapRightKey("UserId");
                     ug.ToTable("UserGroupMembers");
                  });

Please advise. Thanks

like image 735
Antipod Avatar asked Sep 28 '15 11:09

Antipod


2 Answers

I came up with the following solution in .Net Core. In my case I have a base DbContext where I interpret a custom DataAnnotation in order to tell EF to ignore certain properties on my model classes (the functionality I wanted to test).

I could not simply use NotMapped because it caused OData to not be able to see NotMapped properties when specified in the $select, so I needed an alternative way to tell EF to ignore, but not impact OData.

In my case I also created a TestDbContext which inherited from my standard test ModelDbContext class (which in turn inherits from my library DbContextBase class which does the EF Ignore work), but I simply set a property to the provided ModelBuilder so that I could interrogate it during my tests.

    public class TestDbContext : ModelDbContext
    {
      public ModelBuilder ModelBuilder { get; private set; }

      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
        base.OnModelCreating(modelBuilder);
        this.ModelBuilder = modelBuilder;
      }
    }

Then I could test it using the following code:

Note: I had to use reflection to extract the EF internal classes required to determine if indeed the field was set to Ignored. This is normally bad practice, but I could not figure out another way of determining whether EF was ignoring these properties.

    [Fact]
    public void OnModelCreatingTest()
    {
      // Arrange
      DbContextOptionsBuilder<ModelDbContext> builder = new DbContextOptionsBuilder<ModelDbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString());
      var context = new TestDbContext(builder.Options);

      // Act
      // hit the entities to force the model to build
      var testEntity = context.ExampleEntities.FirstOrDefault();
      // use reflection to dig into EF internals
      var builderProperty = typeof(EntityTypeBuilder).GetProperty(
          "Builder",
          System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

      // Assert
      // validate that the extra ExampleEntity fields are ignored in EF (based on their data annotation)
      context.ModelBuilder.Entity(
        typeof(ExampleEntity),
        b =>
        {
          InternalEntityTypeBuilder baseBuilder = (InternalEntityTypeBuilder)builderProperty.GetValue(b);
          Assert.True(baseBuilder.IsIgnored("Property1", ConfigurationSource.Convention));
          Assert.True(baseBuilder.IsIgnored("Property2", ConfigurationSource.Convention));
        });
    }
like image 111
brenwebber Avatar answered Oct 13 '22 12:10

brenwebber


To build on vappolinario's answer, I used a Mock<DbModelBuilder> and passed that into the TestableDbContext, and then verified that the mock was called correctly.

I am using Moq and xUnit in my example

[Fact]
public void MyContext_HasCorrectDefaultSchema()
{
   var mockModel = new Mock<DbModelBuilder>();
   var context = new TestableMyContext();

   context.TestOnModelCreation(mockModel.Object);

   mockModel.Verify(m => m.HasDefaultSchema("public"), Times.Once());
}
like image 29
plasmaTonic Avatar answered Oct 13 '22 14:10

plasmaTonic