Тестирование функции с глобальной переменной stati c в Ceedling - PullRequest
1 голос
/ 09 мая 2020

Как я могу написать тест в Ceedling для функции, которая использует глобальную переменную stati c? Я хотел бы протестировать каждое возможное значение переменной, чтобы добиться хорошего тестового покрытия.

//Pseudo code for file_under_test.c
static int global_var;

int func_under_test(){ 

   switch(global_var){
    case x:
      return some_value;
    case y:
      return some_other_value;
    .
    .
    .
    .
    default:
      return something; 
   }

}

Ответы [ 4 ]

0 голосов
/ 20 августа 2020

Я использовал оболочку, которая включает исходный файл C и добавляет несколько помощников для тестирования. Итак, у вас есть неизменный исходный c исходный код, но доступ ко всем необходимым внутренним компонентам.

файл wrapped_for_test. h

#include <file_under_test.h>
void set_for_test(int value);

файл wrapped_for_test. c

#include <file_under_test.c>

void set_for_test(int value)
{
  global_var = value;
}
0 голосов
/ 10 мая 2020

Этот вопрос на самом деле не имеет никакого отношения к Ceedling (или Unity, CMock и т. Д. c), но я скорее думаю, что это пример интерпретации слова «unit» очень специфическим c способом . Краткая версия моего ответа заключается в том, что пример функции, которую вы здесь написали, на самом деле не представляет собой автономную «единицу», поэтому я бы сказал, что она на самом деле не является «модульной-тестируемой».

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

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

Пример функции полагается на побочные эффекты некоторой другой функции (той, которая имеет побочный эффект изменения вашей stati c «глобальной» переменной), поэтому «правильный модуль» "в этом случае необходимо включить любую имеющуюся у вас функцию, которая вызывает эти побочные эффекты. Я предполагаю, что в вашем файле уже есть хотя бы одна такая функция, или ваш примерный код никогда не вернет ничего другого *.

* Это если только ваш пример не имеет побочного эффекта изменения самой переменной stati c. В этом случае должна быть как минимум функция, которая сбрасывает «глобальное состояние», иначе ваши тесты не будут изолированы друг от друга (т.е. их сложно сделать независимыми от порядка). Лучшим решением было бы явно показать зависимость вашего состояния с помощью аргументов func_under_test, например func_under_test(struct some_opaque_type *state), и добавить функцию struct some_opaque_type *init_for_func_under_test().

TL; DR : If ваша функция не является чистой функцией (например, она полагается на скрытое состояние и / или сама имеет побочные эффекты) или если у вас нет соответствующих «швов» (например, заглушек или шпионов), тогда также включите функции, которые могут изменять скрытое состояние или проверять побочные эффекты в вашем определении тестируемого модуля .

0 голосов
/ 14 мая 2020

Это очень распространенная проблема в модульном тестировании кода C, и наиболее распространенное решение, о котором я знаю, - это исключить ключевое слово stati c во время тестирования. Это требует некоторого планирования, и это трудно сделать в устаревшем коде, но любая stati c, с которой я планирую тестировать, заменяется какой-либо другой строкой. Обычно STATI C или еще лучше TESTABLE_STATI C.

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

//Pseudo code for file_under_test.c
#ifdef TEST
#define TESTABLE_STATIC 
#else
#define TESTABLE_STATIC static
#endif


TESTABLE_STATIC int global_var;

int func_under_test(){ 

   switch(global_var){
    case x:
      return some_value;
    case y:
      return some_other_value;
    .
    .
    .
    .
    default:
      return something; 
   }

}

Затем в в вашем тестовом файле вы просто рассматриваете переменную как глобальную

// your ceedling test 
#include <your_functions.h>

extern int global_var;
void test_function_under_test(void)
{
    // your test code setting global_var as needed
    global_var = some_test_value;
    TEST_ASSERT_EQUAL(expected_val, func_under_test());
}

Я обычно скрываю TESTABLE_STATI C в файле заголовка проекта или если у вас есть файл datatypes.h, поэтому он обычно доступен везде в моем project.

Это также работает для модульного тестирования ваших функций c stati в модуле перевода.

0 голосов
/ 09 мая 2020

Вы можете создать тестовую вспомогательную функцию в file_under_test.c, которая будет настраивать global_var перед вызовом func_under_test(). При необходимости этот помощник тестовой функции может быть скомпилирован только для целей тестирования (с использованием спецификаций c #ifdef), чтобы он не поставлялся с остальной частью кода, если ваш код создается, например, для продукта.

file_under_test.h:

void set_for_test(int value);

file_under_test. c

#ifdef TESTS
void set_for_test(int value)
{
  global_var = value;
}
#endif

test_file. c:

#include <assert.h>
#include "file_under_test.h"

// some other tests
set_for_test(3);
assert (func_under_test() == something);
//...
...