如何对Entity Framework Code First的映射进行单元测试?

12
我正在使用Code First将类映射到一个已存在的数据库。我需要一种方法来对这些映射进行单元测试,这些映射是基于约定、属性和Fluent API混合而成。
为了进行单元测试,我需要确认类的属性是否正确地映射到了数据库中的表和列名。这个测试需要针对上下文进行,并应该涵盖Code First的所有配置选项。
在一个非常高的层次上,我希望像以下伪代码一样进行断言:
Assert.IsTrue(context.TableFor<Widget>().IsNamed("tbl_Widget"));
Assert.IsTrue(context.ColumnFor<Widget>(w => w.Property).IsNamed("WidgetProperty"));

1
我不喜欢EF,如果可能的话,请评估使用NHibernate的可能性。使用NHibernate,您可以非常轻松地测试映射。 - Jupaol
@Jupaol 很好知道,我们同时使用EF和NH,对两者都有一些问题。 - STW
3个回答

2
另一个要考虑的想法是使用Linq和ToString()。
例如这样:
context.Widget.Select(c => c.Property).ToString()

将会导致 SQL Server 提供程序出现这种情况:
"SELECT [Var_3].[WidgetProperty] AS [WidgetProperty] FROM [dbo].[Widget]..."

现在我们可以将所有内容隐藏在某个扩展方法中,并解析生成的 SQL,它几乎看起来像您的伪代码:
Assert.IsTrue(context.Widgets.GetSqlColumnNameFor(w => w.Property).IsNamed("WidgetProperty"));

延期草案:
public string GetSqlColumnNameFor<TSource>(this DbSet<T> source, Expression<Func<TSource, TResult>> selector)
{
    var sql = source.Select(selector).ToString();

    var columnName = sql... // TODO : Some regex parsing

    return 
       columnName;
}

同样地,我们可以创建GetSqlTableNameFor()函数。
更新:我决定寻找一些专门的SQL解析器,因此这个解决方案更加通用,显然对于.NET有这样的东西。

http://www.dpriver.com/blog/list-of-demos-illustrate-how-to-use-general-sql-parser/generate-internal-query-parse-tree-in-xml-for-further-processing/


0

我能想到的唯一覆盖每种可能选项的方式是使用 Entity Framework Power Tools 预编译您的 DbContext 的视图,并可能在生成的类型上结合反射和正则表达式来验证所有映射是否与您想要的一样。对我来说听起来相当痛苦。

另一件想到的事情是创建一个围绕 DbModelBuilder 的门面,以拦截并检查通过它的所有内容,但我不知道这是否会处理基于约定的内容。这也听起来很痛苦。

作为不太完整但更容易的替代方案,您可能可以尽可能地转换为基于属性的映射,从而可以创建一个名为 ModelTesting<TEntity> 的基本测试类,其中包括使用反射的几个测试方法来验证 TEntity 具有:

  • 一个 TableAttribute。
  • 每个属性都有一个 ColumnAttribute 或 NotMappedAttribute。
  • 至少一个 KeyAttribute 属性。
  • 每个属性类型都映射到兼容的数据库类型。
你甚至可以根据属性和类的名称强制执行命名约定(对于表格层次类型有一个警告)。 还可以检查外键映射。这是一个基类,您可以从中派生每个模型类型一次,并捕获大多数错误(无论如何,它会捕获我的大部分错误)。
任何无法通过属性表示的内容(如TPH继承等)都变得更难了。 假设您的DbContext不为您生成数据库,则启动DbContext并在Set 上执行FirstOrDefault的集成测试可能涵盖了其中的大部分。

谢谢你的建议,Sean!我正在考虑使用单一配置方法等不那么全面的方法,并且会查看EF Power Tools,我之前并不知道它们。 - STW

0

如果你写了一个方法

public static string ToMappingString(this Widget obj)

你可以通过认可测试 (www.approvaltests.com or nuget) 轻松地进行测试。

这里有一个视频:http://www.youtube.com/watch?v=vKLUycNLhgc

然而,如果你想测试 "My objects save and retrive themselves" 那么这是一个理论测试的完美场所。

基于理论的测试 大多数单元测试采用以下形式:

Given A,B expect C

基于理论的测试是

Given A,B expect Theory

这个方法的美妙之处在于你不需要知道C,所以任何随机生成器都可以使用,也不需要担心A和B采用哪种特定形式。

示例1:测试加法和减法方法

通常你会有类似下面的代码:

Assert.AreEqual(5, Add(2,3));
Assert.AreEqual(9, Add(10,-1));
Assert.AreEqual(10, Add(5,5));
Assert.AreEqual(7, Subtract(10,3));

然而,如果你写了一份理论测试,它会看起来像这样

for(int i = 1; i < 100; i++)
{ 
    int a = random.Next();
    int b = random.Next();
    Assert.AreEqual(a, Subtract(Add(a,b),b, string.Format("Failed for [a,b] = [{0},{1}], a,b));        
}

现在你已经了解了基于理论的测试,你要测试的理论是什么

Given Model A
When A is stored to the database, and retrieved the resulting object is equal to A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接