Junit/Mockito:选择使用模拟或集成测试来运行测试
我正在学习 Mockito。在开始使用模拟对象之前,我进行了一些更像集成测试的单元测试,因此我有一个带有 setUpBeforeClass() 的测试类,如下所示:
@BeforeClass
public static void setUpBeforeClass() throws Exception {
instance = new UserDataAccess();
instance.setDb(new MyDb());
}
现在与模拟对象非常相似,但设置稍微多一些复杂:
@BeforeClass
public static void setupBeforeClass throws Exception {
instance = new UserDataAccess();
MyDb myDb = mock(MyDb.class);
when(...).thenReturn(...);
...
instance.setDb(myDb);
}
我还有一个测试套件,用于在运行测试之前以众所周知的状态加载数据库,这是通过套件调用的第一个测试类完成的。
我的想法是,我不应该丢弃集成测试,因此我正在考虑将测试套件拆分为 UnitTestSuite 和 IntegrationTestSuite。事实上,模拟测试并没有测试所有内容,例如,它们不会测试查询是否正确。
此外,这两个套件之间的唯一区别是初始数据库重置和 setUpBeforeClass() 代码。为了改变一个方法而复制和改变所有的测试类是一种浪费。初始数据库重置很容易跳过,我只是没有在单元测试套件中包含数据库重置测试类。
要拆分单元测试和集成测试,您有什么建议?扩展所有原始类以覆盖静态方法,然后在套件中包含正确的类?
或者其他方法?你是怎么做的,或者你会做什么?
I'm learning Mockito. Before starting to use mock objects I had some Unit tests that were more like integration tests, so I'd have a test class with a setUpBeforeClass() like this:
@BeforeClass
public static void setUpBeforeClass() throws Exception {
instance = new UserDataAccess();
instance.setDb(new MyDb());
}
Now with the mock Object is a lot similar, but the setup is slightly more complicated:
@BeforeClass
public static void setupBeforeClass throws Exception {
instance = new UserDataAccess();
MyDb myDb = mock(MyDb.class);
when(...).thenReturn(...);
...
instance.setDb(myDb);
}
Also I've a test suite that used to load the DB in a well known state before running the tests, and this is done with the first test class called by the suite.
What I'm thinking is that I should not throw away the integration tests, so I was considering to split the test suite into a UnitTestSuite and a IntegrationTestSuite. In fact the mock tests, are not testing everything, for instance they don't test if queries are correct.
Also, the only difference between those two suites, will be the initial DB reset and the setUpBeforeClass() code. It would be a waste to copy and change all the test classes just to change a method. The initial DB reset is easy to skip, I just not include the db reset test class in the Unit test suite.
To split unit and integration tests what do you suggest? Extending all the original classes to override the static method and then including the right class in the suite?
Or other approaches? How are you doing it, or what would you do?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
永远记住,单元测试(使用模拟)背后的想法是挖掘和探索单个类的所有黑暗角落。它们是为了确保类对于您能想到的所有类型的输入都按预期运行。这就是我们使用模拟来执行此操作的原因,因为我们可以对这些模拟进行编程来执行各种操作,如果该类连接到应用程序的其余部分,这些操作可能难以重现。
另一方面,整合则有不同的侧重点。这是为了确保所有班级共同努力以产生预期的结果。因此,在编码时请记住这一点。这就是您所追求的大局。您无需担心各个类的模糊边缘情况,因为这是您模拟的单元测试的工作。但像数据库状态这样的事情正是集成测试如此重要的原因。
我也同意 @stivlo 关于使用 HSQL 等产品进行内存数据库工作的观点。它们既可以加快集成测试速度,又可以帮助确保测试有一个已知的起点。
我建议将单元测试和集成测试保存在单独的目录中,或者至少将名称匹配的单独包中。很简单,因为它可以帮助您记住正在处理的事情。
还要注意测试蠕变。 IE。实例化大量应用程序类的单元测试确实应该转移到集成测试中,而集成测试则对类进行大量详细的变体样式测试 - 单元测试的候选者。特别是对于最后一个,如果存在不应该存在的集成测试,您可以从构建中删除大量时间。
最后,像任何代码一样,测试代码有时需要一点爱和关注。因此,请留意重构机会。话虽这么说,我也看到过一些测试代码变得难以使用,因为它过于笼统和过度设计。请记住,测试代码不是生产代码,因此在大多数情况下,清晰度比工程更重要。这是一种你将通过经验获得的平衡。
Always remember that the idea behind unit tests (using mocks) is to poke and prode all the dark corners of a single class. They are about making sure that the class behaves as expected for all the sorts of input you can think of. Thats why we use mocks for doing this, because we can program those mocks to do all sorts of things that might be difficult to reproduce if the class was hooked up to the rest of the application.
Integration on the other hand, has a different emphasis. It's about making sure that all your classes work together to produce the desired result. So keep that in mind when coding them. It's the big picture that you are after. You don't need to worry about obsure edge cases of individual classes because that's the job of your mocked unit tests. But things like database state are exactly why integration tests are important.
I also agree with @stivlo about using products like HSQL for in memory database work. They can both speed up integration testing and help to ensure a known starting point for a test.
I would suggest keeping your unit tests and integration tests in seperate directories or at least seperate packages named to match. Simple because it helps you to remember what you are dealing with.
Also watch out for test creep. Ie. Unit tests that are instantiating a lot of application classes and really should be moved into integration tests, and integration tests that are doing lots of detailed variation style tests on a class - candidates for a unit test. Particularly with the last you can prune a lot of time off a build if there are integration tests which should not exist.
Finally like any code, test code needs a little love and attention from time to time. So keep an eye out for refactoring opportunities. That being said, I've also seen test code that has become difficult to work with because it's too generalised and over engineered. Remember test code is not production code, so in most cases clarity is far more important than engineering. It's a balance that you will get through experience.
我建议使用内存数据库进行集成测试,而不是模拟对象。
对于单元测试,我假设您使用数据库作为存根来向测试提供一些数据,并在测试调用诸如
saveMyDomainObject()
之类的内容时作为模拟。在第一种情况下,您只能模拟特定测试实际需要的内容,而不是整个数据库设置。对于第二种情况,您应该使用 Mockito 的validate
来检查预期的行为是否正确。I'd suggest to use in-memory database for integration tests, not a mock object.
For unit test I assume you're using database as stub to provide some data into the tests and as a mock when your test calls something like
saveMyDomainObject()
. In the first case you can mock only what you actually need for a particular test, not the whole database setup. For the second case you should use Mockito'svalidate
to check that the expected behavior is correct.