如何测试函数调用顺序
考虑这样的代码:
class ToBeTested { public: void doForEach() { for (vector<Contained>::iterator it = m_contained.begin(); it != m_contained.end(); it++) { doOnce(*it); doTwice(*it); doTwice(*it); } } void doOnce(Contained & c) { // do something } void doTwice(Contained & c) { // do something } // other methods private: vector<Contained> m_contained; }
我想测试一下,如果我用3个值填充向量,我的函数将以正确的顺序和数量被调用。例如,我的测试可能如下所示:
tobeTested.AddContained(one); tobeTested.AddContained(two); tobeTested.AddContained(three); BEGIN_PROC_TEST() SHOULD_BE_CALLED(doOnce, 1) SHOULD_BE_CALLED(doTwice, 2) SHOULD_BE_CALLED(doOnce, 1) SHOULD_BE_CALLED(doTwice, 2) SHOULD_BE_CALLED(doOnce, 1) SHOULD_BE_CALLED(doTwice, 2) tobeTested.doForEach() END_PROC_TEST()
我们如何建议对此进行测试?有什么方法可以使用CppUnit或者GoogleTest框架吗?也许其他一些单元测试框架可以执行这样的测试?
我知道,如果不从这些函数中调用任何调试功能,这可能是不可能的,但至少可以在某些测试框架中自动完成。我不喜欢扫描跟踪日志并检查其正确性。
UPD:我不仅要检查对象的状态,还要检查执行顺序,以避免在最早的阶段出现性能问题(总的来说,我想知道我的代码是否按预期执行了)。
解决方案
回答
一些模拟框架允许我们设置有序期望,这使我们可以准确说出我们按特定顺序期望的函数调用。例如,RhinoMocks for Callows为此。
我不是C ++编码员,所以我不知道C ++可用的功能,但是那是一种可能允许我们尝试执行的工具。
回答
我们可以检查出嘲笑。
回答
我们应该能够使用任何良好的模拟框架来验证对协作对象的调用是以特定顺序完成的。
但是,我们通常不会测试一个方法对同一类的其他方法进行了某些调用……为什么呢?
通常,在测试类时,我们只关心测试其公开可见状态。如果你测试
其他任何事情,测试将阻止我们稍后进行重构。
我可以提供更多帮助,但我认为示例不一致(AddContained方法的实现在哪里?)。
回答
http://msdn.microsoft.com/zh-CN/magazine/cc301356.aspx
这是一篇关于上下文绑定对象的好文章。它包含一些非常高级的内容,但是如果我们不懒惰,并且真的想了解这种事情,那将非常有帮助。
最后,我们将能够编写如下内容:
[CallTracingAttribute()]
公共类TraceMe:ContextBoundObject
{...}
回答
与其尝试找出调用了多少个函数以及以什么顺序调用,不如找到一组以正确的顺序调用事物只能产生预期输出的输入。
回答
我们可以使用ACE(或者类似的)调试框架,并在测试中将调试对象配置为流式传输到文件。然后,我们只需要检查文件即可。
回答
如果我们对性能感兴趣,建议我们编写一个测试性能的测试。
检查当前时间,运行我们关注的方法,然后再次检查时间。断言所花费的总时间少于某个值。
检查以某种顺序调用方法的问题是代码将不得不更改,并且我们不想在这种情况发生时更新测试。我们应该专注于测试实际需求,而不是测试满足该需求的实现细节。
就是说,如果我们确实想测试方法是否按特定顺序调用,则需要执行以下操作:
- 将他们移至另一个班级,称为协作者
- 将这个其他类的实例添加到ToBeTested类中
- 使用模拟框架将ToBeTested上的实例变量设置为Collborator类的模拟
- 调用被测方法
- 使用模拟框架来断言在模拟中以正确的顺序调用了方法。
我不是母语的cpp发言人,所以我无法评论应该使用哪个模拟框架,但是我看到其他一些评论者在这方面添加了他们的建议。