mardi 9 novembre 2021

Dynamically Test All Entity Framework Core DbContext DbSet<> objects with xUnit

I'm using xUnit and FluentAssertions to write an integration test to validate that our models are correctly mapped. We have dozens of EF contexts and for each context, there are one or more DbSet<> properties like this:

public class SomeContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder builder) { // ... }

    public virtual DbSet<User> Users { get; set; }
}

They are all laid out the same way so this would be an ideal candidate for using [MemberData] to get each context dynamically as input to the test and the DbSet<> property, invoke it, and just make sure it doesn't fail doing a simple query. The Container.GetInstance() call is a call to my DI container to get the actual DbContext object:

public class ContextTests
{
    public static IEnumerable<object[]> EntitiesMappedInCode =>
        typeof(DbContextInitializer).Assembly.GetTypes()
            .Where(t => t.IsSubclassOf(typeof(DbContext)) && !t.IsAbstract)
            .SelectMany(dbContextType =>
            {
                var context = (DbContext) Container.GetInstance(dbContextType);
                var entities = context.Model.GetEntityTypes();
                var dbSetProperties = context.GetType().GetProperties().Where(p =>
                    typeof(DbSet<>).IsAssignableFrom(p.PropertyType.GetGenericTypeDefinition()));
                
                return dbSetProperties.Select(dbSet => new [] {context, dbSet.GetValue(context)});
            });


    [Theory]
    [MemberData(nameof(EntitiesMappedInCode))]
    public void EntitiesDefinedInCode_ExistsInDatabase(DbContext context, object dbSetObject)
    {
        var dbSet = dbSetObject as DbSet<dynamic>; 
        dbSet.Invoking(dbSet => Queryable.FirstOrDefault<dynamic>(dbSet))
            .Should().NotThrow<SqlException>("the entity framework model should match the database");
    }
}

The problem is the reflection does not work properly to give back the runtime instance and fails at p.PropertyType.GetGenericTypeDefinition() with an error.

Has anyone dynamically retrieved the DbSet<> properties of contexts and successfully invoked queries on them?





Aucun commentaire:

Enregistrer un commentaire