如何强制生成 EF LINQ 查询而不执行
我有一个很好解耦的应用程序和依赖注入的应用程序,它使用 Entity Framework 4.1 CodeFirst 通过存储库模式公开 IQueryable。在测试存储库客户端时模拟底层数据存储很容易,但是没有捕获特定类别的错误:
存储库客户端可以自由地将自己的 LINQ 谓词、连接等分层在存储库返回的内容之上:
{
_myRepository.FindAll().Where( x => x.Id == 3 && SomeMethod(x.Name) == "Hello" );
}
这种查询将在模拟 _myRepository 的单元测试中成功,因为模拟返回内存中的实体集合,并且 LINQ-to-Objects 很乐意调用方法“SomeMethod”。它将在实际数据存储中失败,因为“SomeMethod”不会转换为 LINQ-to-Entities 中的 SQL。
我正在尝试找出一种方法,既可以模拟数据集,又可以使真正的 EF 查询提供程序生成(但不执行)SQL。为什么?因为测试应该很快,而且如果可能的话,我不希望它们访问真实的数据库。生成 SQL 将消除此类翻译问题。
到目前为止,我还无法弄清楚如何做到这一点,因为在我的单元测试中,我最终无法控制查询何时具体化。我想我需要提供我自己的 IQueryable 版本和各种 LINQ Queryable 扩展方法,或者尝试通过提供程序机制进行挂钩(使用几年前执行缓存/跟踪提供程序的示例)。这些看起来工作量很大。关于如何实现这一目标有什么想法吗?
I have a nicely decoupled app and dependency-injected app that uses Entity Framework 4.1 CodeFirst to expose IQueryable through a repository pattern. It's easy enough to mock the underlying datastore when testing the repository clients, however a certain class of bug is not being caught:
The repository clients are free to layer their own LINQ predicates, joins, etc on top of what the repository returns:
{
_myRepository.FindAll().Where( x => x.Id == 3 && SomeMethod(x.Name) == "Hello" );
}
This kind of query will succeed in a unit test that mocks _myRepository, because mock returns an in-memory collection of entities and LINQ-to-Objects is happy to call the method "SomeMethod". It will fail against the real data-store because "SomeMethod" does not translate to SQL in LINQ-to-Entities.
I'm trying to figure out a way that I can both mock the dataset and cause the real EF query provider to generate (but not execute) the SQL. Why? Because the tests are supposed to be fast and I don't want them hitting a real database if at all possible. Generating the SQL will flush out translation issues like this.
So far, I have not been able to figure out how to do this, because in my unit tests, I am ultimately not in control of when the query gets materialized. I'm thinking I need to either provide my own version of IQueryable and the various LINQ Queryable extension methods or try and hook in via the provider mechanism (using the sample from a couple of years ago that does Caching/Tracing providers.) Both of these seem like a lot of work. Any ideas on how to achieve this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
不,没有办法做到这一点 - 除非您要构建自己的提供程序,而这又不是解决方案,因为您必须测试真正的提供程序,而不是在真实代码中不会使用的自定义实现。我在此处讨论了和 此处。 还有一个相关答案 关于存储库本身。
简而言之,如果不进行数据库映射和持久性,就无法测试数据库映射和持久性。有一些非常奇怪的信念,即测试应用程序意味着编写单元测试。那是错误的定义。测试应用程序意味着编写测试,单元测试只是众多测试类型中的一种,但它们无法测试所有内容。您需要将它们与其他类型的测试结合起来。这种情况的正确方法是集成测试,它不必每次都运行,但可以安排在构建服务器上。
No there is no way to do that - unless you are going to build your own provider which in turn is not the solution because you must test the real provider not a custom implementation which will not be used in the real code. I discussed it here and here. One more related answer about repository itself.
Simply you can't test database mapping and persistence without doing database mapping and persistence. There is some very strange belief that testing application means writing unit tests. That is wrong definition. Testing application means writing tests and unit tests are just one of many test types but they can't test everything. You need to combine them with other types of tests. The correct approach for this scenario can be integration tests which doesn't have to run every time but can be scheduled on the build server.