Подтвердить что ты не робот

Как проверить статическую функцию

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Не доступен ли он, поскольку он является приватным файлу .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() ...

Ответ 3

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

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

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

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

Ответ 4

Нет - вы не можете напрямую протестировать статическую функцию, не модифицируя источник хотя бы немного (то есть определение static в 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);
}

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

Ответ 5

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

static int foo ()
{
   return 3;
}

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

  return 1;
}
#endif

Ответ 6

Статические функции

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

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

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

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

Ответ 7

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

Ответ 8

#define static

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

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

   count += data;
   return count;
}

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

- курт

Ответ 9

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

Мне удалось обойти его, слегка изменив код 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).

Ответ 10

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

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

static int foo(int);

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

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

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

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

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

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

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

Ответ 11

Есть два способа сделать это.

  • Включите исходный файл c в исходный файл тестирования модуля, поэтому статический метод теперь находится в области исходного файла тестирования и может быть вызван.

  • Сделайте трюк:

#define static

в заголовке исходного файла тестирования устройства.

Он преобразует ключевое слово static в "ничего", или я могу сказать, что он удаляет static из вашего исходного кода.

В каком-то модульном инструменте тестирования мы можем использовать опцию конфигурации "pre-processor" для выполнения этого трюка, просто введите конфигурацию:

static=

Инструмент willl преобразует все ключевые слова static в "nothing"

Но я должен сказать, используя эти способы, метод static (или переменная) теряет конкретный смысл.