Модульное тестирование C - возвращение из заглушки изящной процедуры выхода - PullRequest
5 голосов
/ 02 августа 2011

Вот сценарий, который у меня есть.У функции, которую я тестирую, есть условие ошибки, при нажатии которого вызывается изящная функция выхода для освобождения любой глобальной памяти, закрытия дескрипторов и выхода из программы.

Очевидно, я захочу написать тестэто щекочет это условие, чтобы убедиться, что оно обрабатывается правильно, но я не хочу, чтобы изящная процедура выхода действительно выходила из программы, так как это остановило бы все оставшиеся тесты.Это означает заглушение изящной процедуры выхода.Проблема с заглушкой и отсутствием вызова exit заключается в том, что поток управления возвращается к тестируемой функции (что плохо, поскольку подпрограмма должна была завершиться).

Вот актуальный вопрос: как вернуть управлениеот заглушки к тесту, а не к тестируемой функции?

Я могу сделать setjmp / longjmp, но, поскольку "gotos" в целом плохи, я бы с удовольствием принял любые другие предложения.(Имейте в виду, что это процедурный C, а не C ++, поэтому исключения не будут работать, насколько я знаю)

EDIT Как Сорен и другие предложили ниже, делая выходничего не делать, когда тестирование - отличная идея.Есть несколько способов сделать это, будь то с помощью оператора #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) - это способсделать это, хотя не очень элегантно.Любые идеи о том, как решить эту проблему?

EDIT # 2

Как уже упоминалось в fizzer, setjmp / longjmp - верный способ справиться с этой ситуацией.Вполне вероятно, что я справлюсь с этим навсегда.

Однако я получил другое возможное решение от коллеги.Вместо # определения процедуры gracefulExit () для процедуры-заглушки, выполните следующее:

#define gracefulExit return NULL

Тестируемая конкретная функция прекрасно справляется с этим, поскольку NULL является допустимым возвращаемым значением для нее.Я еще не проверял это в каждом возможном случае (например, функция, которая имеет возвращаемое значение void).Как упоминалось ранее, я, вероятно, буду использовать метод setjmp / longjmp для решения этой проблемы, но если это дополнительное решение вызывает у кого-то идею, прекрасно!

Ответы [ 5 ]

3 голосов
/ 02 августа 2011

Я бы предложил, чтобы ваш метод очистки создал неопубликованный интерфейс для выхода.

так

   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 голос
/ 02 августа 2011

Я использую setjmp / longjmp (обернутый в удобный макрос) для этого и аналогичных сценариев (например, проверить, что утверждение не выполнено).

0 голосов
/ 02 августа 2011

Этого можно достичь, используя слабые символы, если ваша платформа поддерживает их.

Вы бы написали две версии функции graceful_exit(), одну из которых использует обычное приложение, определяемое какслабый символ и другой, используемый при работе под вашей структурой модульного тестирования, определенной как обычно.

Предполагается, что если вы будете ссылаться на свой код модульного теста, будет использоваться сильная версия функции вместослабая версия.

Преимущество заключается в том, что для поддержки юнит-тестирования не требуется никакой условной компиляции.

В this * есть полезное обсуждение использования слабых символов.1011 * вопрос.

0 голосов
/ 02 августа 2011

Как сказал Джонфен, я думаю, что использование препроцессора - способ сделать этоЧто-то вроде:

if (bad_stuff_happened) {
  do_cleanup();
#ifdef UNIT_TEST
  return;
#endif
}
0 голосов
/ 02 августа 2011

Вы можете рассмотреть возможность использования блока #define, чтобы указать, когда выполняются ваши процедуры «очистки и выхода», и компилировать его только при сборке для производства или выпуска. Таким образом, вы все равно можете увидеть, что условие выполнено, но на самом деле оно не выполнит код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...