我如何单元测试实体框架代码优先映射?

时间:2012-04-25 20:49:28

标签: c# unit-testing ef-code-first entity-framework-4.3

我正在使用Code First将类映射到现有数据库。我需要一种方法来对这些映射进行单元测试,这些映射是基于约定,基于属性和流畅的api的混合。

要进行单元测试,我需要确认类的属性映射到数据库中正确的表名和列名。此测试需要针对上下文执行,并且应首先涵盖代码的所有配置选项。

在非常高的层面上,我想要断言像(伪代码):

Assert.IsTrue(context.TableFor<Widget>().IsNamed("tbl_Widget"));
Assert.IsTrue(context.ColumnFor<Widget>(w => w.Property).IsNamed("WidgetProperty"));

3 个答案:

答案 0 :(得分:1)

要考虑的另一个想法是使用Linq和ToString()。

例如:

context.Widget.Select(c => c.Property).ToString()

将导致SQL Server提供程序:

"SELECT [Var_3].[WidgetProperty] AS [WidgetProperty] FROM [dbo].[Widget]..."

现在我们可以在一些Extension方法中隐藏它,并解析生成的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 Parsers,所以这个解决方案更通用,显然.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/

答案 1 :(得分:0)

我能想到涵盖所有可能选项的唯一方法是使用Entity Framework Power Tools预编译DbContext的视图,并且可能使用生成类型的反射和生成的RegEx的组合代码本身以验证所有地图以您希望的方式映射。听起来很痛苦。

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

作为一个不太完整但更容易的替代方案,你可以通过尽可能切换到基于属性的映射来淘汰其中很大一部分。这将允许您创建一个基本测试类,比如ModelTesting&lt; TEntity&gt;,其中包含一些使用反射来验证TEntity的测试方法:

  • 单个TableAttribute。
  • 每个属性都有一个ColumnAttribute或NotMappedAttribute。
  • 至少一个具有KeyAttribute的属性。
  • 每种属性类型都映射到兼容的数据库类型。

您甚至可以根据属性和类的名称强制执行命名约定(对每个层次结构的表类型有一个警告)。也可以检查外键映射。这是一个一次写入的基类,你可以从每个模型类型中获得一次,并捕获大部分错误(好吧,无论如何,它都占据了我的大多数)。

任何不能由属性表示的东西,比如TPH继承等,都会变得有点困难。如果您的DbContext不为您生成数据库,那么启动DbContext并在Set&lt; TEntity&gt;()上执行FirstOrDefault的集成测试可能会覆盖大部分基数。

答案 2 :(得分:0)

如果你写了一个方法

public static string ToMappingString(this Widget obj)

然后您可以通过批准测试(www.approvaltests.com或nuget)轻松测试这个

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

但是,如果您要测试“我的对象保存并自行检索” 那么这是“基于理论的测试”的理想之地

基于理论的测试 大多数单元测试采用

的形式
Given A,B expect C

基于理论的测试

Given A,B expect Theory

这样的美妙之处在于无需担心哪种特殊形式A&amp; B因为你不需要知道C,所以任何随机发生器都可以工作。

示例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