C单元测试 - 从存根的正常退出例程返回

时间:2011-08-01 21:37:24

标签: c unit-testing

这是我得到的情景。我正在测试的函数有一个错误条件,如果命中,它会调用一个正常的退出函数来释放任何全局内存,关闭句柄并退出程序。

显然,我想要编写一个测试来解决这个问题,以确保它被正确处理但我不希望优雅的退出例程实际退出程序,因为这会停止任何剩余的测试。这意味着剔除优雅的退出例程。存根和不调用exit的问题是控制流返回到被测函数(由于例程应该退出,因此很糟糕。)

这是一个实际的问题:我们如何将控制从短线函数返回到测试而不是被测试的函数?

我可以做一个setjmp / longjmp,但由于“gotos”一般不好,我会喜欢任何其他的建议。 (请记住,这是程序C,而不是C ++,因此就我所知,异常不会起作用)

修改 正如Soren和其他人在下面提出的那样,当测试是一个好主意时,退出是不行的。无论是通过#define语句还是exit()例程的存根,有几种方法可以做到这一点。

然而,这样做会带来一个问题,即我真的在解决之后(除了setjmp / longjmp)。看看这个场景:

void gracefulExit() {
    // Clean Up
    exit();
}

void routineUnderTest() {
    // Do some calcs

    if (someBadCondition == TRUE)
        gracefulExit()

    // Do some more calcs
}

如果exit()在这种情况下什么都不做,gracefulExit()会将控制权返回给测试中的例程,这不应该发生。因此,我需要一种方法使exit()(或gracefulExit()的存根版本返回控件而不是测试中的函数。

setjmp / longjmp(又名goto)是一种方法,虽然不是一种优雅的方式。关于如何解决的任何想法?

编辑#2

正如fizzer提到的,setjmp / longjmp是处理这种情况的有效方法。这是我永久处理它的可能方式。

然而,我已经从同事那里得到了另一种可能的解决方案。而不是将gracefulExit()例程#defining到存根例程,请执行以下操作:

#define gracefulExit return NULL

测试中的特定函数处理这个很好,因为NULL是它的有效返回值。我还没有在每种可能的情况下对此进行测试(例如,具有void返回值的函数)。如前所述,我可能会使用setjmp / longjmp方法来解决这个问题,但如果这个额外的解决方案为某人提出了一个想法,那太好了!

5 个答案:

答案 0 :(得分:3)

我建议您的清理方法创建一个未发布的界面来执行退出。

所以

   void (*exitfunc((int) = exit;
   void myCleanUp() {
      .... do the cleanup
      (*exitfunc)(-1); // this will in normal operation call exit
   }

并在您的单元测试代码中,您将“退出”功能“覆盖”为

   void donothing(int exitcode) {}
   unittest(){
      extern void (*exitfunc((int);
      exitfunc = donothing; // or use a longjump if clean exit cannot be made without
      ... do the test.....

这意味着无论是在单元测试中还是在其他地方,您的代码都会编译成相同的行为,并且只有在执行单元测试时才会出现行为差异。使用条件编译进行单元测试的替代方法意味着你最终会得到.o文件,你不知道它们是用于测试还是生产,你的项目会发生不好的事情。

答案 1 :(得分:1)

我使用setjmp / longjmp(包含在一个方便的宏中)用于此类和类似场景(例如测试断言失败)。

答案 2 :(得分:0)

您可以考虑使用#define块来指定运行“清理和退出”例程的时间,并仅在构建生产或发布时进行编译。这样,你仍然可以看到条件是否命中,但它实际上不会执行代码。

答案 3 :(得分:0)

像jonfen说的那样,我认为使用预处理器就是这样做的。类似的东西:

if (bad_stuff_happened) {
  do_cleanup();
#ifdef UNIT_TEST
  return;
#endif
}

答案 4 :(得分:0)

如果您的平台支持弱符号,您可以通过使用弱符号来实现此目的。

您将编写graceful_exit()函数的两个版本,一个由正常应用程序定义为弱符号使用,另一个在正常定义的单元测试框架下运行时使用。

目的是如果你链接你的单元测试代码,那么将使用强大的函数版本而不是弱版本。

这样做的好处是不需要条件编译来支持您的单元测试。

关于在this问题中使用弱符号的讨论很有用。