有没有办法阻止 Maven Test 重建数据库?
最近我被要求有效地推销我的部门的单元测试。我无法告诉你这让我有多兴奋,但我确实有一个担忧。我们将 JUnit 与 Spring 和 Maven 结合使用,这意味着每次调用 mvn test
时,它都会重建数据库。显然,我们无法将其与我们的生产服务器集成——这会杀死有价值的数据。
如何在不告诉 Maven 跳过测试的情况下防止重建?
我能想到的最好的办法就是分配脚本在测试数据库中运行(为了可读性而添加换行符):
mvn test
-Ddbunit.schema=<database>test
-Djdbc.url=jdbc:mysql://localhost/<database>test?
createDatabaseIfNotExist=true&
useUnicode=true&characterEncoding=utf-8
我忍不住认为一定有更好的方法。
我特别有兴趣了解是否有一种简单的方法可以告诉 Maven 仅在特定类上运行测试而不构建其他任何东西? mvn -Dtest=<测试名称> test
仍然重建数据库。
=======更新=======
我脸上有一点鸡蛋。我没有意识到我在两个地方使用了相同的变量,这意味着 POM 使用“skip.test”变量来重建数据库和运行测试......
I've recently been asked to, effectively, sell my department on unit testing. I can't tell you how excited this makes me, but I do have one concern. We're using JUnit with Spring and Maven, and this means that each time mvn test
is called, it rebuilds the database. Obviously, we can't integrate that with our production server -- it would kill valuable data.
How do I prevent the rebuilding without telling maven to skip testing?
The best I could figure was to assign the script to operate in a test database (line breaks added for readability):
mvn test
-Ddbunit.schema=<database>test
-Djdbc.url=jdbc:mysql://localhost/<database>test?
createDatabaseIfNotExist=true&
useUnicode=true&characterEncoding=utf-8
I can't help but think there must be a better way.
I'm especially interested in learning if there is an easy way to tell Maven to only run tests on particular classes without building anything else? mvn -Dtest=<test-name> test
still rebuilds the database.
======= update =======
Bit of egg on my face here. I didn't realize that I was using the same variable in two places, meaning that the POM was using a "skip.test" variable for both rebuilding the database and for running the tests...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
更新:我猜想 DBUnit 会重建数据库,因为它在测试设置方法中被告知要这样做。如果更改设置方法,则可以消除数据库重建。当然,您应该这样做,以便在需要时重置数据库,并在不需要时忽略它。我的第一个赌注是使用系统属性来控制它。您可以按照与
jdbc.url
等相同的方式在命令行上设置该属性。然后在设置方法中添加一个if
来测试该属性,并在设置时重置数据库。如果可以的话,与生产数据库完全分离的测试数据库绝对是最好的选择。您甚至可以使用例如 Derby,这是一个可以嵌入 JVM 中运行的内存数据库。但如果您绝对不能拥有单独的数据库,请至少在该数据库内使用单独的测试模式。
在这种情况下,我建议您将数据库连接参数放入 pom 内的配置文件中,默认为测试数据库,并使用单独的配置文件来包含生产设置。这样,您就不会意外地针对生产数据库运行测试。
然而,一般来说,了解针对数据库运行的测试并不是严格意义上的真正的单元测试,而是集成测试,这一点也很重要。如果您有一组现有的此类测试,那么可以尽可能多地使用它们。但是,您应该尝试添加更多真正的单元测试,这些单元测试一次仅测试代码的一小部分(最多是一个方法或类),理想情况下是自包含的(不需要数据库、网络、配置文件等)。 )这样他们就可以跑得快——这是非常重要的一点。如果您有 5000 个单元测试,每个单元测试只需要 5 秒来运行,那么总共需要将近 7 个小时,所以您显然不会经常运行它们。如果测试只需要 5 毫秒,那么您将在不到半分钟内获得结果,因此您可以在提交最新更改之前运行所有测试 - 每天多次。这使得您从测试中获得反馈的速度产生了巨大的差异。
希望这有帮助。
Update: I guess that DBUnit does the rebuilding of the DB because it is told to do so in the test setup method. If you change your setup method, you can eliminate the DB rebuild. Of course, you should do it so that you get the DB reset when you need it, and omit it when you don't. My first bet would be to use a system property to control this. You can set the property on the command line the same way you already do with
jdbc.url
et al. Then in the setup method you add anif
to test for that property and do the DB reset if it is set.A test database, completely separated from your production DB is definitely the best choice if you can have it. You can even use e.g. Derby, an in-memory DB which can run embedded within the JVM. But in case you absolutely can't have a separate DB, use at least a separate test schema inside that DB.
In this scenario I would recommend you put your DB connection parameters into profiles within your pom, the default being the test DB, and a separate profile to contain the production settings. This way it can never happen that you accidentally run your tests against the production DB.
In general, however, it is also important to understand that tests run against a DB are not really unit tests in the strict sense, rather integration tests. If you have an existing set of such tests, fine, use them as much as you can. However, you should try to move towards adding more real unit tests, which test only a small, isolated portion of your code at once (a method or class at most), ideally self contained (need no DB, net, config files etc.) so they can run fast - this is a very important point. If you have 5000 unit tests and each takes only 5 seconds to run, that totals up to almost 7 hours, so you obviously won't run them very often. If a test takes only 5 milliseconds, you get the results in less than half a minute, so you can afford to run all your tests before you commit your latest change - many times a day. That makes a huge difference in the speed of feedback you get from the tests.
Hope this helps.
Maven 本身不会对数据库执行任何操作,而是您的代码。无论如何,针对生产数据库运行测试(不是单元测试)是非常不寻常的。
如果没有更多详细信息,很难说(您没有显示任何内容),但个人资料可能是一种可行的方法。
Maven doesn't do anything with databases by itself, your code does. In any case, it's very unusual to run tests (which are not unit tests) against a production database.
Hard to say without more details (you're not showing anything) but profiles might be a way to go.
根据定义,单元测试仅对系统中的单个组件进行操作。您不应尝试编写与任何外部服务(Web、数据库等)集成的单元测试。我对此的解决方案是使用一个好的模拟框架来消除组件所具有的任何依赖项的行为。这鼓励了良好的接口 API,因为大多数模拟框架最适合使用简单的接口。最好为与数据库的任何交互创建一个存储库模式接口,然后在测试与其交互的类时模拟该实现。然后,您可以单独测试您的存储库实现的功能。这还有一个额外的好处,那就是保持单元测试足够快,以保留 CI 的一部分,以便您的反馈周期尽可能快。
Unit tests, by definition, only operate on a single component in the system. You should not be attempting to write unit tests which integrate with any external services (web, DB, etc.). The solution I have to this is to use a good mocking framework to stub out the behaviour of any dependencies your components have. This encourages good interface APIs since most mocking frameworks work best with simple interfaces. It would be best to create a Repository pattern interface for any interactions with your DB and then mock out the impl any time you are testing a class that interacts with it. You can then functionally test your Repository impl separately. This also has the added benefit of keeping your unit tests fast enough to remain part of your CI so that your feedback cycle is as fast as possible.