使用 xUnit 动态测试所有 Entity Framework 核心 DbContext DbSet<> 对象
Dynamically Test All Entity Framework Core DbContext DbSet<> objects with xUnit
我正在使用 xUnit 和 FluentAssertions 编写集成测试来验证我们的模型是否正确映射。我们有几十个 EF 上下文,每个上下文都有一个或多个 DbSet<>
属性,如下所示:
public class SomeContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder builder) { // ... }
public virtual DbSet<User> Users { get; set; }
}
它们都以相同的方式布局,因此这将是使用 [MemberData]
动态获取每个上下文作为测试输入的理想选择,DbSet<>
属性,调用它,并确保它不会在执行简单查询时失败。 Container.GetInstance()
调用是对我的 DI 容器的调用,以获取实际的 DbContext
对象:
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");
}
}
问题是反射无法正常返回运行时实例,并在 p.PropertyType.GetGenericTypeDefinition()
处失败并出现错误。
有没有人动态检索上下文的 DbSet<>
属性并成功调用它们的查询?
抛出异常是因为 GetGenericTypeDefinition()
不适用于非泛型类型,因此您应该在调用它之前首先检查类型是否真的是泛型:
var dbSetProps = typeof(HwContext).GetProperties().Where(c =>
c.PropertyType.IsGenericType &&
typeof(DbSet<>).IsAssignableFrom(c.PropertyType.GetGenericTypeDefinition()));
也不要转换为 DbSet<dynamic>
,只需使用 dynamic
:
foreach (var prop in dbSetProps)
{
dynamic dbSet = prop.GetValue(context);
// this should not throw
Queryable.FirstOrDefault(dbSet);
}
多亏了 Evk,我才能让它像这样工作:
public class ContextTests
{
public static IEnumerable<object[]> EntitiesMappedInCode =>
typeof(DbContextInitializer).Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(DbContext)) && !t.IsAbstract)
.SelectMany(dbContextType =>
{
var dbSetProps = dbContextType.GetProperties()
.Where(c => c.PropertyType.IsGenericType
&& typeof(DbSet<>).IsAssignableFrom(c.PropertyType.GetGenericTypeDefinition()));
var context = Container.GetInstance(dbContextType);
return dbSetProps.Select(dbSetProp => new [] {context, dbSetProp.GetValue(context)});
});
[Theory]
[MemberData(nameof(EntitiesMappedInCode))]
public void EntitiesDefinedInCode_ExistsInDatabase(DbContext context, dynamic dbSet)
{
context.Invoking(dbContext => Queryable.FirstOrDefault(dbSet))
.Should().NotThrow<SqlException>("the entity framework model should match the database");
}
}
我正在使用 xUnit 和 FluentAssertions 编写集成测试来验证我们的模型是否正确映射。我们有几十个 EF 上下文,每个上下文都有一个或多个 DbSet<>
属性,如下所示:
public class SomeContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder builder) { // ... }
public virtual DbSet<User> Users { get; set; }
}
它们都以相同的方式布局,因此这将是使用 [MemberData]
动态获取每个上下文作为测试输入的理想选择,DbSet<>
属性,调用它,并确保它不会在执行简单查询时失败。 Container.GetInstance()
调用是对我的 DI 容器的调用,以获取实际的 DbContext
对象:
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");
}
}
问题是反射无法正常返回运行时实例,并在 p.PropertyType.GetGenericTypeDefinition()
处失败并出现错误。
有没有人动态检索上下文的 DbSet<>
属性并成功调用它们的查询?
抛出异常是因为 GetGenericTypeDefinition()
不适用于非泛型类型,因此您应该在调用它之前首先检查类型是否真的是泛型:
var dbSetProps = typeof(HwContext).GetProperties().Where(c =>
c.PropertyType.IsGenericType &&
typeof(DbSet<>).IsAssignableFrom(c.PropertyType.GetGenericTypeDefinition()));
也不要转换为 DbSet<dynamic>
,只需使用 dynamic
:
foreach (var prop in dbSetProps)
{
dynamic dbSet = prop.GetValue(context);
// this should not throw
Queryable.FirstOrDefault(dbSet);
}
多亏了 Evk,我才能让它像这样工作:
public class ContextTests
{
public static IEnumerable<object[]> EntitiesMappedInCode =>
typeof(DbContextInitializer).Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(DbContext)) && !t.IsAbstract)
.SelectMany(dbContextType =>
{
var dbSetProps = dbContextType.GetProperties()
.Where(c => c.PropertyType.IsGenericType
&& typeof(DbSet<>).IsAssignableFrom(c.PropertyType.GetGenericTypeDefinition()));
var context = Container.GetInstance(dbContextType);
return dbSetProps.Select(dbSetProp => new [] {context, dbSetProp.GetValue(context)});
});
[Theory]
[MemberData(nameof(EntitiesMappedInCode))]
public void EntitiesDefinedInCode_ExistsInDatabase(DbContext context, dynamic dbSet)
{
context.Invoking(dbContext => Queryable.FirstOrDefault(dbSet))
.Should().NotThrow<SqlException>("the entity framework model should match the database");
}
}