我听说在测试EF时,由于LINQ to Objects和LINQ to Entities提供程序之间的差异,您需要对实时数据库使用集成测试。
为什么我们不能在不实际执行的情况下对查询的构造进行单元测试?我原本以为你可以将你的IQueryable附加到LINQ to Entities提供程序以某种方式并确认SQL是否正确生成(使用ToTraceString这样不执行实际查询的东西)。
我想象沿着这些行的代码(这个查询在L2O中运行良好但在L2E中运行不正确):
<Test()> _
Public Sub Query_Should_Build_Against_L2E()
Dim testQuery = From d In myDb
Where d.Status = CType(Status.Ready, Integer)
testQuery.SetQueryProvider("L2E")
Assert.DoesNotThrow(testQuery.ToTraceString())
End Sub
修改
我尝试按如下方式实施GertArnold的解决方案:
Dim context As New Context("Data Source=fakedbserver;Initial Catalog=fakedb;Persist Security Info=True;")
Dim result = context.myTable.Where(Function(d) d.Status=True)
抛出ProviderIncompatibleException
,并显示消息“从数据库获取提供程序信息时发生错误。这可能是由Entity Framework使用不正确的连接字符串引起的。检查内部异常以获取详细信息并确保连接字符串是正确的。“这是完整的例外链:
System.Data.ProviderIncompatibleException:从数据库获取提供程序信息时发生错误。这可能是由实体框架使用不正确的连接字符串引起的。检查内部异常以获取详细信息,并确保连接字符串正确。
System.Data.ProviderIncompatibleException:提供程序未返回ProviderManifestToken字符串。
System.InvalidOperationException:此操作需要连接到“master”数据库。无法创建与“主”数据库的连接,因为已打开原始数据库连接并且已从连接字符串中删除凭据。提供未打开的连接。
System.Data.SqlClient.SqlException:建立与SQL Server的连接时发生与网络相关或特定于实例的错误。服务器未找到或无法访问。验证实例名称是否正确,以及SQL Server是否配置为允许远程连接。 (提供者:命名管道提供程序,错误:40 - 无法打开与SQL Server的连接)
答案 0 :(得分:2)
将您的IQueryable附加到LINQ to Entities提供程序
问题是:哪IQueryable
?接口本身就没有了。使用EF,您正在处理实现IQueryable
的类,但除此之外还要做更多。事实上,他们完全有能力与上下文和查询提供商合作,已经附加。 IQueryable.IQueryProvider
是一个只读属性,因此它由工厂设置,用于创建特定的IQueryable
实现。
因此,您的testQuery
将是ObjectQuery
,因为它来自myDb
,而不是EF上下文。
为什么我们不能在不实际执行的情况下对查询的构造进行单元测试?
这不是你的实际问题吗?事实上,我甚至想知道你是否想要一个不同的查询提供者。我想你会希望EF查询提供程序这样做,否则仍然不能保证与EF一样工作。
无论如何,您可以在其连接字符串中创建一个带有假数据库名称的上下文(以确保它不连接),并检查((ObjectQuery)testQuery).ToTraceString()
的有效性。只要你不迭代testQuery
就行了。我可以想象,当在复杂的执行路径中组合查询时,这种测试有一些价值。 (但我宁愿避免这样做。)
答案 1 :(得分:0)
我能够创建一个模拟DbContext,它“连接”到正确的EF提供者而没有任何实际的数据库连接。这可以保证正在测试的LINQ实际上可以在针对您的真实数据库的集成场景中工作。它目前仅针对MS SQL Server实现,但它应该很容易扩展到其他EF提供程序。我已经发布了下面的完整代码,我很高兴听到有关它的反馈。
编辑我将代码移到GPL CodePlex project。
在查看GertArnold的答案并重新思考整个问题(以及对来源的广泛反思)后,我想出了我认为可行的解决方案。问题是DbContext的默认配置是:
SqlConnectionFactory
创建与nameOrConnectionString
构造函数的DbContext
属性所请求的数据库的连接CreateDatabaseIfNotExists
作为默认上下文初始化程序)DbConnection.Open
和DbConnection.State
)System.Data.Entity.Internal.InternalContext.QueryForModel
,然后在活动连接上创建命令以查询模型的数据库)System.Data.Common.DbProviderServices
和System.Data.Common.DbProviderManifest
对象,这些对象共同支持将表达式转换为SQL 。这些步骤中的前四个(需要实际的数据库连接)可以创建一个新的IDatabaseInitializer
,它在InitializeDatabase
方法中什么都不做,然后用{{1注册测试中的上下文类型}}
Database.SetInitializer
和SqlProviderManifest
并将工作传递给它们(我甚至可以指定按年使用的SQL服务器版本。)