模拟框架和高测试覆盖率重要吗?
模拟框架,例如 EasyMock,可以更轻松地插入虚拟依赖项。话虽如此,使用它们来确保如何调用特定组件上的不同方法(以及以什么顺序)对我来说似乎很糟糕。它将行为暴露给测试类,这使得维护生产代码变得更加困难。我真的看不出有什么好处;精神上我感觉自己被拴在一个沉重的球上。
我更喜欢只针对接口进行测试,将测试数据作为输入并断言结果。更好的是,使用一些自动生成测试数据来验证给定属性的测试工具。例如,将一个元素添加到列表中,然后立即删除它会产生相同的列表。
在我们的工作场所,我们使用 Hudson 提供测试覆盖范围。不幸的是,这很容易让人盲目地痴迷于一切都经过测试。我强烈认为,如果想在维护模式下保持高效,就不应该测试所有内容。 Web 框架中的控制器就是一个很好的例子。一般来说,它们应该包含很少的逻辑,在我看来,使用控制器按特定顺序调用这样那样的方法的模拟框架进行测试是没有意义的。
亲爱的SOer们,您对此有何看法?
Mock frameworks, e.g. EasyMock, make it easier to plugin dummy dependencies. Having said that, using them for ensuring how different methods on particular components are called (and in what order) seems bad to me. It exposes the behaviour to test class, which makes it harder to maintain production code. And I really don't see the benefit; mentally I feel like I've been chained to a heavy ball.
I much rather like to just test against interface, giving test data as input and asserting the result. Better yet, to use some testing tool that generates test data automatically for verifying given property. e.g. adding one element to a list, and removing it immediately yields the same list.
In our workplace, we use Hudson which gives testing coverage. Unfortunately it makes it easy to get blindly obsessed that everything is tested. I strongly feel that one shouldn't test everything if one wants to be productive also in maintenance mode. One good example would be controllers in web frameworks. As generally they should contain very little logic, testing with mock framework that controller calls such and such method in particular order is nonsensical in my honest opinion.
Dear SOers, what are your opinions on this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我读到了 2 个问题:
您对测试以特定顺序调用组件上的特定方法有何看法?
我过去曾犯过这个问题。如今,我们使用更多的“存根”和更少的“嘲笑”。
我们尝试编写只测试一件事的单元测试。当我们这样做时,通常可以编写一个非常简单的测试来存根
与大多数其他组件的交互。我们很少主张顺序。这有助于减少测试的脆弱性。
仅测试一件事的测试更容易理解和维护。
另外,如果您发现自己必须为与大量组件的交互编写大量期望,那么您正在测试的代码很可能存在问题。如果维护测试很困难,那么您正在测试的代码通常可以重构。
人们应该痴迷于测试覆盖率吗?
在为给定类编写单元测试时,我非常痴迷于测试覆盖率。它使我很容易发现我尚未测试的重要行为。我还可以判断哪些部分不需要涵盖。
总体单元测试覆盖率统计数据?只要它们很高,就不会特别感兴趣。
整个系统 100% 的单元测试覆盖率?一点也不感兴趣。
I read 2 questions:
What is your opinion on testing that particular methods on components are called in a particular order?
I've fallen foul of this in the past. We use a lot more "stubbing" and a lot less "mocking" these days.
We try to write unit tests which test only one thing. When we do this it's normally possible to write a very simple test which stubs out
interactions with most other components. And we very rarely assert ordering. This helps to make the tests less brittle.
Tests which test only one thing are easier to understand and maintain.
Also, if you find yourself having to write lots of expectations for interactions with lots of components there could well be a problem in the code you're testing anyway. If it's difficult to maintain tests the code you're testing can often be refactored.
Should one be obsessed with test coverage?
When writing unit tests for a given class I'm pretty obsessed with test coverage. It makes it really easy to spot important bits of behaviour that I haven't tested. I can also make a judgement call about which bits I don't need to cover.
Overall unit test coverage stats? Not particularly interested so long as they're high.
100% unit test coverage for an entire system? Not interested at all.
我同意 - 我赞成严重倾向于状态验证而不是行为验证(对 经典 TDD,同时仍然使用测试替身)。
单元测试的艺术一书在这些领域提供了很多好的建议。
100% 的测试覆盖率、GUI 测试、测试 getter/setter 或其他无逻辑代码等似乎不太可能提供良好的投资回报率。在任何情况下,TDD 都将提供高测试覆盖率。测试什么可能会破坏。
I agree - I'm in favor of leaning heavily towards state verification rather than behavior verification (a loose interpretation of classical TDD while still using test doubles).
The book The Art of Unit Testing has plenty of good advice in these areas.
100% test coverage, GUI testing, testing getters/setters or other no-logic code, etc. seem unlikely to provide good ROI. TDD will provide high test coverage in any case. Test what might break.
这取决于您如何对程序的域进行建模。
如果您根据存储在数据结构中的数据和从一个数据结构读取数据并将派生数据存储在另一数据结构中的方法(过程或函数取决于您的设计的过程或功能)中的数据来建模域,那么模拟对象是不合适的。所谓的“基于状态”的测试就是您想要的。您关心的结果是一个过程将正确的数据放入正确的变量中,而它所调用的实现方法只是一个实现细节。
如果您按照对象协作所依据的消息传递通信协议来对域进行建模,那么这些协议就是您所关心的,而对象存储哪些数据来协调它们在它们所扮演的角色的协议中的行为只是实现细节。在这种情况下,模拟对象是完成这项工作的正确工具,并且基于状态的测试将测试与不重要的实现细节联系得太紧密。
在大多数面向对象的程序中,都有多种风格。有些代码将被编写为纯粹的函数式,转换不可变的数据结构。其他代码将协调对象的行为,随着时间的推移改变其隐藏的内部状态。
至于高测试覆盖率,它实际上并没有告诉你那么多。低测试覆盖率表明测试不足,但高测试覆盖率并不表明代码经过充分测试。例如,测试可以通过代码路径运行,从而增加覆盖率统计数据,但实际上不会对这些代码路径的作用做出任何断言。此外,真正重要的是程序的不同部分如何组合运行,哪些单元测试覆盖率不会告诉您。如果您想验证您的测试是否确实充分测试了系统的行为,您可以使用突变测试工具。这是一个缓慢的过程,因此您需要在夜间构建中运行它,而不是在每次签入时运行。
It depends on how you model the domain(s) of your program.
If you model the domains in terms of data stored in data structures and methods that read data from one data structure and store derived data in another data structure (procedures or functions depending how procedural or functional your design is), then mock objects are not appropriate. So called "state-based" testing is what you want. The outcome you care about is that a procedure puts the right data in the right variables and what it calls to make that happen is just an implementation detail.
If you model the domains in terms of message-passing communication protocols by which objects collaborate, then the protocols are what you care about and what data the objects store to coordinate their behaviour in the protocols in which they play roles is just implementation detail. In that case, mock objects are the right tool for the job and state based testing ties the tests too closely to unimportant implementation details.
And in most object-oriented programs there is a mix of styles. Some code will be written purely functional, transforming immutable data structures. Other code will be coordinating the behaviour of objects that change their hidden, internal state over time.
As for high test coverage, it really doesn't tell you that much. Low test coverage shows you where you have inadequate testing, but high test coverage doesn't show you that the code is adequately tested. Tests can, for example, run through code paths and so increase the coverage stats but not actually make any assertions about what those code paths did. Also, what really matters is how different parts of the program behave in combination, which unit test coverage won't tell you. If you want to verify that your tests really are testing your system's behaviour adequately you could use a Mutation Testing tool. It's a slow process, so it's something you'd run in a nightly build rather than on every check-in.
我问过类似的问题多少单元测试是一件好事< /a>,这可能有助于了解人们认为合适的各种测试级别。
I'd asked a similar question How Much Unit Testing is a Good Thing, which might help give an idea of the variety of levels of testing people feel are appropriate.
在代码维护期间,某些初级员工会破坏运行“控制器按特定顺序调用此类方法”的代码部分的概率是多少?
在代码维护期间,某些初级员工破坏运行“控制器按特定顺序调用此类方法”的
如果发生这种情况,您的组织会付出什么代价 - 生产中断、调试/修复/重新测试/重新发布、法律/财务风险、声誉风险等......?
现在,将#1 和#2 相乘,然后检查您不愿意实现合理数量的测试覆盖率是否值得冒这个风险。
有时,它不会(这就是为什么在测试中存在收益递减点的概念)。
例如,如果您维护一个对生产不重要的 Web 应用程序,并且有 100 个用户,如果该应用程序损坏,他们有解决方法(和/或可以轻松立即回滚),那么花费 3 个月对该应用程序进行全面测试覆盖可能是毫无意义。
如果您开发的应用程序中的一个小错误可能会造成数百万美元或更严重的后果(想想航天飞机软件或巡航导弹的制导系统),那么全面覆盖的彻底测试就变得更有意义。
另外,我不确定我是否对您的问题读得太多,但您似乎暗示启用模拟的单元测试在某种程度上排除了应用程序/集成功能测试。如果是这种情况,您反对这种想法是正确的 - 两种测试方法必须共存。
What is the probability that during your code's maintenance some junior employee will break the part of code that runs "controller calls such and such method in particular order"?
What is the cost to your organization if such a thing occurs - in production outage, debugging/fixing/re-testing/re-release, legal/financial risk, reputation risk, etc...?
Now, multiply #1 and #2 and check whether your reluctance to achieve a reasonable amount of test coverage is worth the risk.
Sometimes, it will not be (this is why in testing there's a concept of a point of diminishing returns).
E.g. if you maintain a web app that is not production critical and has 100 users who have a workaround if the app is broken (and/or can do easy and immediate rollback), then spending 3 months doing full testing coverage of that app is probably non-sensical.
If you work on an app where a minor bug can have multi-million-dollar or worse consequences (think space shuttle software, or guidance system for a cruise missile), then the thorough testing with complete coverage becomes a lot more sensical.
Also, i'm not sure if i'm reading too much into your question but you seem to be implying that having mocking-enabled unit testing somehow excluds application/integration functional testing. If that is the case, you are right to object to such a notion - the two testing approaches must co-exist.