Как проверить статическую функцию - PullRequest
37 голосов
/ 27 февраля 2009

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

Ответы [ 12 ]

48 голосов
/ 27 февраля 2009

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

#include "code_under_test.c"
...test framework...

То есть я включаю весь файл, содержащий тестируемую функцию, в тестовый комплект. Это последнее средство, но оно работает.

13 голосов
/ 27 февраля 2009

Можете ли вы дать больше информации о том, почему вы не можете вызвать функцию?

Разве это не доступно, потому что это личное для файла .c? Если это так, лучше всего использовать условную компиляцию, которая предоставляет доступ к функции, чтобы другие модули компиляции могли получить к ней доступ. Например

SomeHeaderSomewher.h

#if UNIT_TEST
#define unit_static 
#else
#define unit_static static
#endif

foo.h

#if UNIT_TEST
void some_method
#endif

foo.cpp

unit_static void some_method() ...
7 голосов
/ 27 февраля 2009

Для модульных тестов у нас фактически есть тестовый код в самом исходном файле, и мы условно компилируем его при тестировании. Это дает модульным тестам полный доступ ко всем функциям и переменным уровня файла (статическим или другим).

Сами модульные тесты не являются статичными - это позволяет нам вызывать модульные тесты из одной программы супер-тестирования, которая проверяет все модули компиляции.

Когда мы отправляем код, мы условно компилируем модульные тесты, но на самом деле в этом нет необходимости (если вы хотите быть уверены, что вы отправляете точно того же кода, который вы тестировали).

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

5 голосов
/ 27 февраля 2009

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

Вы можете создать отдельную функцию в тестовом файле, которая просто вызывает статическую функцию?

Например:

//Your fn to test
static int foo(int bar)
{
  int retVal;
  //do something
  return retVal;
}

//Wrapper fn
int test_foo(int bar)
{
  return foo(bar);
}

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

4 голосов
/ 27 февраля 2009

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

Вывод (возвращаемые значения / побочные эффекты) публичной функции должен использоваться для проверки эффекта статичности.

Это означает, что вам нужно иметь соответствующие заглушки, чтобы «поймать» эти побочные эффекты. (например, если функция вызывает файловый ввод-вывод, вам необходимо предоставить заглушки для переопределения этих файловых функций ввода-вывода). Лучший способ сделать это - сделать каждый набор тестов отдельным проектом / исполняемым файлом и избегать ссылок на какие-либо внешние функции lib. Вы можете издеваться даже над функциями C, но это не стоит усилий.

Во всяком случае, этот подход я использовал до сих пор, и он работает для меня. Удачи

3 голосов
/ 28 января 2015

Если вы находитесь в среде Unix, вы можете включить в тестовый файл дополнительный заголовок yourheader_static.h с объявлениями ваших статических функций и перевести файл obj code_under_test.o через objdump --globalize-symbols=syms_name_file для глобализации локальных символов. Они будут видны, как если бы они были нестатическими функциями.

2 голосов
/ 07 октября 2016
#define static

Это очень плохая идея. Если у вас есть переменная, объявленная локальной для функции, она меняет поведение функции. Пример:

static int func(int data)
{
   static int count = 0;

   count += data;
   return count;
}

Вы можете вызвать функцию из модульного теста, так как функция func () будет экспортирована, однако основные функции кода будут изменены.

- курт

2 голосов
/ 27 февраля 2009

Вы можете добавить нестатическую функцию для вызова статической функции, а затем вызвать нестатическую функцию.

static int foo ()
{
   return 3;
}

#ifdef UNIT_TEST
int test_foo ()
{
  if (foo () == 3)
    return 0;

  return 1;
}
#endif
1 голос
/ 14 июня 2017

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

Скажите, что ваша функция для тестирования

static int foo(int);

Вы создаете еще один заголовочный файл с именем testing_headers.h, который будет иметь содержимое -

static int foo(int);
int foo_wrapper(int a) {
    return foo(a);
}

Теперь при компиляции вашего файла c для тестирования вы можете принудительно включить этот заголовок из параметров компилятора.

Для clang и gcc флаг равен --include. Для компилятора Microsoft C это /FI.

Это потребует абсолютно 0 изменений в вашем файле c, и вы сможете написать нестатическую оболочку для вашей функции.

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

Затем можно вызвать функцию, используя этот глобальный указатель на функцию.

1 голос
/ 14 июня 2017

Если вы используете Ceedling и пытаетесь использовать метод #include "code_under_test.c", сборка теста не удастся, потому что она автоматически попытается создать "code_under_test.c" один раз, когда #included, а также потому, что это цель теста.

Мне удалось обойти это путем небольшого изменения кода code_under_test.c и нескольких других изменений. Оберните весь файл code_under_test.c этой проверкой:

#if defined(BUILD)
...
#endif // defined(BUILD)

Добавьте это в свой тестовый комплект:

#define BUILD
#include "code_under_test.c"

Добавьте определение BUILD в ваш файл конфигурации Makefile или проекта:

# Makefile example
..
CFLAGS += -DBUILD
..

Ваш файл теперь будет собираться из вашей среды и при включении из вашего тестового жгута. Ceedling теперь не сможет собрать файл второй раз (убедитесь, что ваш файл project.yml НЕ определяет BUILD).

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