简体   繁体   English

用C ++进行单元测试

[英]unit tests in C++

Is okay to break all dependencies using interfaces just to make a class testable? 是否可以使用接口来破坏所有依赖项以使类可测试? It involves significant overhead at runtime because of many virtual calls instead of plain method invocations. 由于许多虚拟调用而不是普通的方法调用,它在运行时会产生很大的开销。

How does test driven development work in real world C++ applications? 测试驱动的开发如何在现实世界的C ++应用程序中运行? I read Working Effectively With Legacy Code and fond it quite useful but do not get up to speed practising TDD. 我读过有效使用遗留代码并且非常有用,但是没有加快练习TDD的速度。

If I do a refactoring it occurs very often that I have to completely rewite unit test because of massive logic changes. 如果我进行重构,它经常发生,因为大量的逻辑变化,我必须完全重新进行单元测试。 My code changes very often change the fundamental logic of the processing of data. 我的代码更改经常改变数据处理的基本逻辑。 I do not see a way to write unit tests which do not have to change in a large refactoring. 我没有看到编写单元测试的方法,这些测试不需要在大型重构中进行更改。

May be someone can point me to an open source c++ application wich is using TDD to learn by example. 可能有人可以指向我使用TDD通过示例学习的开源c ++应用程序。

Update : See this question too. 更新 :也看到这个问题。

I can only answer some parts here: 我只能在这里回答一些部分:

Is okay to break all dependencies using interfaces just to make a class testable? 是否可以使用接口来破坏所有依赖项以使类可测试? It involves significant overhead at runtime because of many virtual calls instead of plain method invocations. 由于许多虚拟调用而不是普通的方法调用,它在运行时会产生很大的开销。

If your performance will suffer too much because of it, no (benchmark!). 如果你的表现会因此受到太大影响,那就没有(基准!)。 If your development suffers too much, no (estimate extra effort). 如果您的开发受到太多影响,请不要(估计额外的努力)。 If it seems like it won't matter much and help in the long run and helps you with quality, yes. 从长远来看,它似乎无关紧要,有助于提高质量,是的。

You could always 'friend' your test classes, or a TestAccessor object through which your tests could investigate stuff within it. 您可以随时“交朋友”您的测试类,或TestAccessor对象,您的测试可以通过该对象调查其中的内容。 That avoids making everything dynamically dispatchable just for testing. 这避免了为了测试而使一切动态可调度。 (it does sound like quite a bit of work.) (听起来确实有点工作。)

Designing testable interfaces isn't easy. 设计可测试的接口并不容易。 Sometimes you have to add some extra methods that access the innards just for testing. 有时你必须添加一些额外的方法来访问内部只是为了测试。 It makes you cringe a bit but it's good to have and more often than not those functions are useful in the actual application as well, sooner or later. 这会让你感到畏缩,但这样做很好,而且这些功能在实际应用中也很常见,迟早也是如此。

If I do a refactoring it occurs very often that I have to completely rewite unit test because of massive logic changes. 如果我进行重构,它经常发生,因为大量的逻辑变化,我必须完全重新进行单元测试。 My code changes very often change the fundamental logic of the processing of data. 我的代码更改经常改变数据处理的基本逻辑。 I do not see a way to write unit tests which do not have to change in a large refactoring. 我没有看到编写单元测试的方法,这些测试不需要在大型重构中进行更改。

Large refactorings by defintion change a lot, including the tests. 通过定义的大型重构改变了很多,包括测试。 Be happy you have them as they will test stuff after refactoring too. 很高兴你有他们,因为他们将在重构后测试东西。

If you spend more time refactoring than making new features, perhaps you should consider thinking a bit more before coding to find better interfaces that can withstand more changes. 如果你花费更多时间进行重构而不是制作新功能,那么在编码之前你应该考虑多考虑一下,找到能够承受更多变化的更好的接口。 Also, writing unit-tests before interfaces are stable is a pain, no matter what you do. 此外,无论您做什么,在接口稳定之前编写单元测试都是一件痛苦的事。

The more code you have against an interface that changes much, the more code you will have to change each time. 对于变化很大的接口,你拥有的代码越多,每次都需要更改的代码就越多。 I think your problem lies there. 我认为你的问题就在那里。 I've managed to have sufficiently stable interfaces in most places, and refactor only parts now and then. 我已经设法在大多数地方拥有足够稳定的接口,并且偶尔只重构部分。

Hope it helps. 希望能帮助到你。

I've routinely used macros, #if and other preprocessor tricks to "mock-out" dependencies for the purpose of unit testing in C and C++, exactly because with such macros I don't have to pay any run-time cost when the code is compiled for production rather than testing. 为了在C和C ++中进行单元测试,我经常使用宏, #if和其他预处理器技巧来“模拟”依赖关系,正是因为有了这样的宏,我不需要支付任何运行时的成本。代码是为生产而不是测试而编译的。 Not elegant, but reasonably effective. 不优雅,但相当有效。

As for refactorings, they may well require changing the tests when they are so overwhelmingly large and intrustive as you describe. 至于重构,他们可能需要更改测试,因为它们如你所描述的那么庞大且具有侵入性。 I don't find myself refactoring so drastically all that often, though. 不过,我并没有发现自己如此彻底地重构。

The obvious answer would be to factor out dependencies using templates rather than interfaces. 显而易见的答案是使用模板而不是接口来分解依赖关系。 Of course that might hurt compile-times (depending on exactly how you implement it), but it should eliminate the runtime overhead at least. 当然,这可能会损害编译时间(具体取决于您实现它的方式),但至少应该消除运行时开销。 A slightly simpler solution might be to just rely on a set of typedefs, which can be swapped out with a few macros or similar. 一个稍微简单的解决方案可能是仅仅依赖一组typedef,这些typedef可以与一些宏或类似的东西交换出来。

As to your first question - it's rarely worthwhile to break things just for the sake of testing, although sometimes you might have to break things before making them better as part of your refactoring. 关于你的第一个问题 - 为了测试而破坏事物是很有必要的,尽管有时你可能需要先破坏一些东西,然后再将它们作为重构的一部分。 The most important criteria for a software product is that it works, not that it's testable. 软件产品最重要的标准是它可以工作,而不是它是可测试的。 Testable is only important as it helps you make a product that is more stable and functions better for your end-users. 可测试只是重要的,因为它可以帮助您使产品更稳定,并为您的最终用户提供更好的功能。

A big part of test-driven development is selecting small, atomic parts of your code that aren't likely to change for unit testing. 测试驱动开发的一个重要部分是选择代码的小原子部分,这些部分不太可能因单元测试而改变。 If you're having to rewrite a lot of unit tests because of massive logic changes, you might need to test at a finer-grained level, or re-design your code so that it's more stable. 如果由于大量逻辑更改而不得不重写大量单元测试,则可能需要在更细粒度级别进行测试,或者重新设计代码以使其更稳定。 A stable design shouldn't change drastically over time, and testing won't help you avoid massive refactoring is that becomes required. 稳定的设计不应随着时间的推移而发生剧烈变化,测试不会帮助您避免大规模重构。 However, if done right testing can make it so that when you refactor things you can be more confident that your refactoring was successful, assuming that there are some tests that don't need to be changed. 但是,如果完成正确的测试可以使得当您重构事物时,您可以更加确信您的重构是成功的,假设有一些测试不需要更改。

Is okay to break all dependencies using interfaces just to make a class testable? 是否可以使用接口来破坏所有依赖项以使类可测试? It involves significant overhead at runtime because of many virtual calls instead of plain method invocations. 由于许多虚拟调用而不是普通的方法调用,它在运行时会产生很大的开销。

I think it is OK to break the dependencies, since that will lead into better interfaces. 我认为打破依赖关系是可以的,因为这将导致更好的接口。

If I do a refactoring it occurs very often that I have to completely rewite unit test because of massive logic changes. 如果我进行重构,它经常发生,因为大量的逻辑变化,我必须完全重新进行单元测试。 My code changes very often change the fundamental logic of the processing of data. 我的代码更改经常改变数据处理的基本逻辑。 I do not see a way to write unit tests which do not have to change in a large refactoring. 我没有看到编写单元测试的方法,这些测试不需要在大型重构中进行更改。

You won't get ride of these large refactorings in any language since your tests should be expressing the real intent of your code. 您不会以任何语言使用这些大型重构,因为您的测试应该表达您的代码的真实意图。 So if the logic changes, your tests must change. 因此,如果逻辑发生变化,您的测试必须改变。

Maybe you aren't really doing TDD, like: 也许你并没有真正做TDD,比如:

  1. Create a test that fails 创建失败的测试
  2. Create the code to pass the test 创建代码以通过测试
  3. Create another test that fails 创建另一个失败的测试
  4. Fix the code to pass both tests 修复代码以通过两个测试
  5. Rinse and repeat until you think you have enough tests that show what your code should be doing 冲洗并重复,直到您认为有足够的测试来显示您的代码应该做什么

These steps say that you should be doing minor changes, and not big ones. 这些步骤说你应该做一些小改动,而不是大改变。 If you stay with the latter, you can't escape big refactors. 如果你留在后者,你无法逃脱大型重构。 No language will save you from that, and C++ will be the worst of them because of the compile times, link times, bad error messages, etc. etc. 没有语言可以帮助你避免这种情况,由于编译时间,链接时间,错误消息等等,C ++将是最糟糕的。

I'm actually working in a real world software written in C++ with a HUGE legacy code under it. 我实际上是在用C ++编写的真实世界软件中工作,其下有一个巨大的遗留代码。 We're using TDD and it is really helping evolve the design of the software. 我们正在使用TDD,它确实有助于改进软件的设计。

If I do a refactoring it occurs very often that I have to completely rewrite unit test because of massive logic changes. 如果我进行重构,它经常发生,因为大量的逻辑变化,我必须完全重写单元测试。 ... I do not see a way to write unit tests which do not have to change in a large refactoring. ...我没有看到编写单元测试的方法,这些测试不需要在大型重构中进行更改。

There are multiple layers of testing , and some of those layers won't break even after big logic changes. 有多层测试 ,其中一些层甚至在大的逻辑变化后也不会破坏。 Unit test, on the other hand, are meant to test the internals of methods and objects, and will need to change more often than that. 另一方面,单元测试旨在测试方法和对象的内部,并且需要更频繁地进行更改。 There's nothing wrong, necessarily. 一定没有错。 It's just how things are. 事情就是如此。

Is [it] okay to break all dependencies using interfaces just to make a class testable? 是否可以使用接口来破坏所有依赖项以使类可测试?

It's definitely OK to design classes to be more testable . 设计类更可测试是绝对可以的 That's part of the purpose of TDD, after all. 毕竟,这是TDD目的的一部分。

It involves significant overhead at runtime because of many virtual calls instead of plain method invocations. 由于许多虚拟调用而不是普通的方法调用,它在运行时会产生很大的开销。

Just about every company has some list of rules that all employees are supposed to follow. 几乎每家公司都有一些所有员工应该遵循的规则清单。 The clueless companies simply list every good quality they can think of ("our employees are efficient, responsible, ethical, and never cut corners"). 无能为力的公司只列出他们能想到的每一个优质产品(“我们的员工高效,负责,道德,永不偷工减料”)。 More intelligent companies actually rank their priorities. 更聪明的公司实际上排名优先考虑。 If somebody comes up with an unethical way to be efficient, does the company do it? 如果有人想出一种不道德的方式来提高效率,公司会这样做吗? The best companies not only print up brochures saying how the priorities are ranked, but they also make sure management follows the ranking. 最好的公司不仅打印出小册子,说明优先级如何排名,而且还确保管理层遵循排名。

It is entirely possible for a program to be efficient and easily testable. 程序完全可以高效且易于测试。 However there are times when you need to choose which is more important. 但有时您需要选择哪个更重要。 This is one of those times. 这是其中一次。 I don't know how important efficiency is to you and your program, but you do. 我不知道效率对你和你的计划有多重要,但是你做到了。 So "would you rather have a slow, well-tested program, or a fast program without total test coverage?" 所以“你宁愿做一个缓慢,经过良好测试的程序,还是没有全面测试覆盖的快速程序?”

It involves significant overhead at runtime because of many virtual calls instead of plain method invocations. 由于许多虚拟调用而不是普通的方法调用,它在运行时会产生很大的开销。

Remember that it is only a virtual call overhead if you access the method through a pointer (or ref.) to you interface or object. 请记住,如果通过指针(或参考)访问接口或对象,则它只是一个虚拟调用开销。 If you access the method through a concrete object in the stack, it will not have the virtual overhead, and it can even be inlined. 如果通过堆栈中的具体对象访问该方法,则它将不具有虚拟开销,甚至可以内联。

Also, never assume that this overhead is big before profiling your code. 此外,在分析代码之前,永远不要假设这种开销很大。 Almost always, a virtual call is worthless if you compare to what your method is doing. 几乎总是,如果与您的方法正在进行比较,虚拟调用就毫无价值。 (Most of the penalty comes from not being possible to inline a one-line method, not from the extra indirection of the call). (大多数惩罚来自无法内联单行方法,而不是来自额外的呼叫间接)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM