在没有静态测试数据库的情况下,如何使 DAO 类的单元测试不那么脆弱?
情况如下:
我正在开发一个 DAO 对象,它使用 hibernate criteria API 来形成许多复杂的查询,以在数据库上执行某些任务(例如跨多个字段的关键字搜索)。
我们需要对其进行单元测试,以确保生成的查询对于各种场景都是正确的。测试它的一种方法(这可能是更好的方法)是通过在最后检查它并模拟数据库交互来测试休眠标准是否正确创建。然而,这是不可取的,因为首先它有点作弊(它只是重复代码将要执行的操作),而且它不会检查标准本身是否会导致休眠状态呕吐,或者当它进入数据库时会导致问题。
然后使用的选项是针对测试数据库运行查询。然而,由于历史原因,没有静态测试数据库(例如,代码作为代码的一部分签入的数据库),并且我的项目的职权范围不允许我开始创建一个静态测试数据库,我们必须满足于针对定期使用生产数据刷新的共享开发数据库。
当这些刷新发生时,测试背后的数据也可能发生变化,这将使我们的单元测试变得脆弱。我们可以通过在测试中不使用精确的数字来克服这个问题,但这并不是真正足够的测试。
那么问题是:在这种情况下,人们会做什么来减少测试的脆弱性?我想到的一个选择是运行执行相同查询的本机 SQL(行为上 - 它不必与 hibernate 生成的查询完全相同)来获取预期的数字,然后运行 DAO 版本来查看如果匹配的话。这样,查询的行为就可以始终在初始本机 SQL 中实现,并且您将始终获得正确的数字。
任何有关此问题或其他有关如何管理这种情况的想法的反馈将不胜感激。
答
:更新:
关于 hsqldb/h2/derby 建议,我很熟悉它们,但该公司还没有准备好走这条路,只在一个测试用例上零敲碎打地做是不会的。不适合。
关于我之前的建议,我想详细说明一下 - 考虑这种情况:
我想确保我相对复杂的关键字搜索返回 2100 个“John Smith”匹配项。
为了找到预期的数字,我会分析我的数据库并使用 SQL 查询找出该数字。将该查询作为测试的一部分以便您始终知道您正在测试条件的行为有什么缺点?
所以基本上问题是:如果由于某种原因您无法拥有用于测试的静态数据集,您将如何以非脆弱的方式执行集成测试?
Here's the scanario:
I am working on a DAO object which uses hibernate criteria API to form a number of complicated queries to perform certain tasks on the database (keyword search across multiple fields for example).
We need to unit test this to ensure that the generated query is correct for various scenarios. One way of testing it -which could be preferable- would be to test the hibernate criteria is created correctly by checking it at the end and mocking the database interaction. However this is not desirable as firstly it's kinda cheating (it's merely duplicating what the code would be doing) and also it doesn't check if the criteria itself causes hibernate to barf or when it goes to database it causes issues.
The option to use is then run the query against a test database. However, for historical reasons there is no static test database (one that code be checked in as part of the code for example) and the remit of my project does not allow me to embark on creating one, we have to content with testing against a shared development database that's periodically refreshed with production data.
When theses refreshes happen, the data behind the tests could change too, and this would make our unit tests brittle. We can get over it by not using exact numbers in tests but it's not really adequate testing that way.
The question is then: what do people do in cases like this to make tests less brittle? One option that I have in mind is to run a native SQL that does the same query (behaviourally - it doesn't have to be exact same as the query generated by hibernate) to get the expected number and then run the DAO version to see if it matches. This way, the behaviour of the query can be always implemented in the initial native SQL and you will always have the correct numbers.
Any feedback on this or other ideas on how to manage this situation would be greatly appreciated.
A.
UPDATE:
With regards to hsqldb/h2/derby suggestions, I am familiar with them but the company is not ready to go down that route just yet and doing it piecemeal on just one test case won't be suitable.
With regards to my earlier suggestion I would like to elaborate a bit more - consider this scenario:
I want to ensure that my relatively complicated keyword search returns 2100 matches for "John Smith".
In order to find the expected number, I would have analyzed my database and found out the number using a SQL Query. What is the downside of having that query as part of the test, so that you will always know the you are testing the behaviour of the criteria?
So basically the question is: if for some reason you could not have a static data set for testing, how would you perform you integration tests in a non-brittle way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一种方法是使用内存数据库,例如 Apache Derby 或 HSQLDB,并在测试开始之前使用 DBUnit 预先填充数据。
更新:这是一个很好的关于该方法的文章。
One approach could be to use in-memory database like Apache Derby or HSQLDB, and prepopulate it with data before test start using DBUnit.
UPDATE: Here is a nice article about the aproach.
我同意 Andrey 和 Bedwyr 的观点,即从长远来看,最好的方法是创建一个专门用于测试的 hsqldb 数据库。如果您无法选择这样做,那么您的解决方案似乎是合适的。您无法测试所有内容,但您也不想什么都不测试。我已经多次使用这种方法来针对集成数据库等测试 Web 服务。但请记住,如果您添加新列等,也必须维护该数据库。
您必须决定要测试的内容。您不想测试 hibernate,您不想测试数据库是否满足您的要求(就 SQL 而言)。在您的测试中,您可以假设 hibernate 可以正常工作,数据库也可以正常工作。
你说:
为什么要根据您给出的标准进行冬眠呕吐?因为你给了它错误的标准。这不是休眠的问题,而是创建条件的代码的问题。您可以在没有数据库的情况下进行测试。
到数据库的时候有问题吗?一般来说,Hibernate 会创建适合您提供的条件和数据库方言的 sql,因此,任何问题都与条件有关。
数据库与休眠所期望的不匹配?现在您正在测试标准和数据库是否一致。为此,您需要一个数据库。但您不再测试标准,您正在测试一切是否一致,这是一种不同类型的测试。
所以实际上,在我看来,您正在进行集成测试,从标准到数据库结构的整个链都有效。这是一个完全有效的测试。
因此,我所做的就是在测试中创建另一个与数据库(jdbc)的连接以获取信息。我执行 SQL 来获取行数等,或者检查是否发生了插入。
我认为你的方法是完全有效的。
I agree with Andrey and Bedwyr that the best approach in the long term is to create an hsqldb database specifically for testing. If you don't have the option of doing that, then your solution seems like an appropriate one. You can't test everything, but you don't want to test nothing either. I've used this approach a few times for testing web services against integration databases etc. But remember that this database has to be maintained as well, if you add new columns etc.
You have to decide what you're trying to test. You don't want to test hibernate, you don't want to test that the database is giving what you've asked for (in terms of SQL). In your tests, you can assume that hibernate works, as does the database.
You say:
Why should hibernate barf on the criteria you give it? Because you're giving it the wrong criteria. This is not a problem with hibernate, but with the code that is creating the criteria. You can test that without a database.
It has problems when it gets to the database? Hibernate, in general, creates the sql that is appropriate to the criteria and database dialect you give it, so again, any problem is with the criteria.
The database does not match what hibernate is expecting? Now you are testing that the criteria and the database are aligned. For this you need a database. But you're not testing the criteria any more, you're testing that everything is aligned, a different sort of test.
So actually, it seems to me you're doing an integration test, that the whole chain from the criteria to the structure of the database works. This is a perfectly valid test.
So, what I do is in my tests to create another connection to the database (jdbc) to get information. I execute SQL to get number of rows etc, or check that an insert has happened.
I think your approach is a perfectly valid one.
您所需要做的就是启动< a href="http://www.h2database.com" rel="nofollow">H2 或类似的 - 将一些实体放入其中并执行集成测试。一旦您完成了一些测试,您应该能够提取一个数据设置实用程序,该实用程序创建一个包含一些测试数据的模式,如果您觉得需要,您可以将其用于所有集成测试。
All you need to do is fire up H2 or similar - put some entities in it and execute your integration tests. Once you've done this for a few tests you should be able to extract a data setup utility that creates a schema with some test data that you can use for all the integration tests if you feel the need.