单元测试数据库方法
我目前正在开发使用 SQLite 数据库的 ac# 项目。对于该项目,我需要进行单元测试,但被告知单元测试不应涉及外部文件,例如用于测试的数据库文件,而测试应该模拟数据库。
如果我有一个函数来测试数据库中是否存在某个东西,那么如何通过单元测试来测试这种方法。
I am currently working on a c# project which makes use of an SQLite Database. For the project I am required to do unit testing but was told that unit testing shouldn't involve external files, like database files for the testing and instead the test should emulate the database.
If I have a function that tests if a something exists in a database how could this sort of method be tested with a unit testing.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
在使用数据库的应用程序中,至少有一个组件负责与该数据库进行通信。该组件的单元测试可能涉及模拟数据库,但使用真实数据库测试组件是完全有效的(并且通常是可取的)。毕竟,组件应该封装并代理与该数据库的通信——单元测试应该对此进行测试。有许多策略可以方便地执行此类单元测试 - 有关示例,请参阅侧栏中的相关 SO 问题列表。
避免在单元测试中访问数据库的一般规定适用于非数据库组件。由于非数据库组件的数量通常远远超过与数据库相关的组件,因此绝大多数单元测试不应涉及数据库。事实上,如果此类非数据库组件需要数据库进行有效测试,则可能存在设计问题 - 可能存在不正确的关注点分离。
因此,单元测试应该避免数据库的原则通常是正确的,但这不是绝对的规则。它只是一个有助于构建复杂系统的(强有力的)指南。过于严格地遵循规则会导致很难充分测试封装外部系统的“边界”组件——在这些地方,错误很容易隐藏!因此,当单元测试需要数据库时,人们真正应该问自己的问题是:被测组件是否合法地直接访问数据库,还是应该与另一个负责该职责的组件合作?
同样的推理也适用于单元测试中外部文件和其他资源的使用。
In an application that uses a database, there will be at least one component whose responsibility is to communicate with that database. The unit test for that component could involve a mocked database, but it is perfectly valid (and often desirable) to test the component using a real database. After all, the component is supposed to encapsulate and broker communication with that database -- the unit test should test that. There are numerous strategies to perform such unit tests conveniently -- see the list of related SO questions in the sidebar for examples.
The general prescription to avoid accessing databases in unit tests applies to non-database components. Since non-database components typically outnumber database-related components by a wide margin, the vast majority of unit tests should not involve a database. Indeed, if such non-database components required a database to be tested effectively, there is likely a design problem present -- probably improper separation of concerns.
Thus, the principle that unit tests should avoid databases is generally true, but it is not an absolute rule. It is just a (strong) guideline that aids in structuring complex systems. Following the rule too rigidly makes it very difficult to adequately test "boundary" components that encapsulate external systems -- places in which bugs find it very easy to hide! So, the question that one should really be asking oneself when a unit test demands a database is this: is the component under test legitimately accessing the database directly or should it instead collaborate with another that has that responsibility?
This same reasoning applies to the use of external files and other resources in unit tests as well.
对于 SQLite,您可以使用内存数据库。您可以通过插入数据来暂存数据库,然后对其运行测试。
With SQLite, you could use an in-memory database. You can stage your database by inserting data and then run your tests against it.
一旦数据库介入,它总是会模糊单元测试和集成测试之间的界限。话虽如此,能够将某些内容放入数据库(设置),进行测试并在最后删除它(清理)始终是一个很好且非常有用的测试。这使您可以端到端地测试应用程序的一部分。
就我个人而言,我喜欢以属性驱动的方式来做到这一点。通过指定为每个测试运行的 Sql 脚本作为属性,如下所示。
连接字符串像往常一样来自 app.config,甚至可以是主项目中 app.config 的符号链接。
不幸的是,这不是 Visual Studio 附带的 MS 测试运行程序的标准功能。如果您使用 Postsharp 作为 AOP 框架,那么这很容易做到。如果没有,您仍然可以通过使用 .Net 的一项称为“上下文绑定对象”的功能,获得标准 MS Test Runner 的相同功能。这允许您将自定义代码注入到对象创建链中,以执行类似 AOP 的操作,只要您的对象继承自 ContextBoundObject。
我在这里写了一篇博客文章,其中包含更多详细信息和一个小型完整的代码示例。
http://www.chaitanyaonline.net/2011/09/25/improving-integration-tests-in-net-by-using-attributes-to-execute-sql-scripts/
Once databases get involved it always blurs the line between unit testing and integration testing. Having said that, it is always a nice and very useful test to be able to put something in a database (Setup), Do your test and remove it at the end (Cleanup). This lets you test end to end one part of your application.
Personally I like to do this in an attribute driven fashion. By Specifying the Sql scripts to run for each test as an attribute like so ..
The connectionstrings come from the app.config as usual and can even be a symbolic link to the app.config in your main project.
Unfortunately this isn't a standard feature with the MS test runner that ships with visual studio. If you are using Postsharp as your AOP framework, this is easy to do. If not, you can still get the same functionality for standard MS Test Runner, by using a feature of .Net called "Context Bound Objects". This lets you inject custom code into an object creation chain to do AOP like stuff as long as your objects inherit from ContextBoundObject.
I did a blog post with more details and a small, complete code sample here.
http://www.chaitanyaonline.net/2011/09/25/improving-integration-tests-in-net-by-using-attributes-to-execute-sql-scripts/
我认为进行依赖于数据库信息的单元测试确实是个坏主意。
我还认为使用 sqlite 进行单元测试是一个坏主意。
您需要测试对象协议,因此如果您在测试中需要某些内容,您应该在测试中的某个位置(通常在设置时)创建它们。
由于很难删除持久性,因此流行的方法是使用 SQLite,但始终在单元测试中创建所需的内容。
检查此链接单元测试和数据库我认为这会更有帮助
I think is really bad idea to have unit tests that depends on database information.
Also I think is a bad idea to use sqlite for unit tests.
You need to test objects protocol, so if you need something in your tests you should create them somewhere in the tests (usually at setUp).
Since is difficult to remove persistence, the popular way to do it is using SQLite, but always create what you need in unit tests.
check this link Unit Tests And Databases this will be more helpful I think
最好使用模拟框架来模拟数据库。对于 C#,有实体框架。甚至 SQLite 的使用也是代码的外部依赖。
It's best to use a mocking framework, to mimic a database. For C# there is the Entity Framework. Even the use of sqlite is an outside dependency to your code.
一般来说,如果避免外部文件并且一切都在代码中完成,那么事情会变得更容易。没有规则说“不应该”,有时拥有外部依赖更有意义。但只有在你考虑过如何不拥有它并意识到权衡是什么之后。
其次,布莱恩所说的是一个不错的选择,也是我以前使用过的。
in general it makes life easier if external files are avoided and everything is done in code. There are no rules which says "shouldn't", and sometimes it just makes more sense to have the external dependency. But only after you have considered how not to have it, and realized what the tradeoffs are.
Secondly, what Bryan said is a good option and the one I've used before.