Я нахожусь в проекте, где у нас есть большая кодовая база, и в настоящее время у нее вообще нет модульных тестов. Код, над которым мы работаем, будет, наконец, выполняться на коробке, которая действует как коммутатор / маршрутизатор / межсетевой экран.
Поэтому я работаю над фрагментом кода, который должен быть проверен модулем с помощью Gtest.
проблема, с которой я сталкиваюсь, заключается в том, что я проверяю переменные, чтобы проверить саму функцию.
Например, у меня есть функция, которая использует как 4 указателя на разные объекты и использует пару глобальных переменных. Чтобы протестировать разные пути в коде, мне нужно инициализировать почти все состояние машины / значения зависимых переменных.
В дополнение к сложности, как это верно для большой кодовой базы, эта функция / метод, который я написал, использует множество других процедур / методов, которые также необходимо протестировать. Каждый из них должен быть также протестирован, и каждый из них имеет свои собственные зависимости.
Я не уверен, правильно ли я подхожу к проблеме, или это тот случай, когда gtest может оказаться неподходящим инструментом для тестирования такой большой базы кода.
Если у кого-то есть опыт, скажем, тестирования, скажем, стека вызовов скажем
function A {
code
code
function B
code
code
function C
code
}
function B
{
function D
code
function E
}
function C{
code
function F
function G
code
}
как то так. Как мне проверить все эти функции A-F ?? Что такое хорошая стратегия?
Во-первых, рефакторинг кода, чтобы тестируемые части были изолированы. В частности, это означает удаление доступа к глобалам. Пример:
int global;
int function() {
int r = foo();
global += r / 2;
bar(r);
return 42;
}
Удаление глобального объекта означает преобразование его во входной параметр:
int real_function(int* target) {
assert(target);
int r = foo();
*target += r / 2;
bar(r);
return 42;
}
Тогда, конечно, оставшийся код перестанет компилироваться, поэтому вы добавляете ключ обратной совместимости:
int global_bar;
// @deprecated, use real_function() directly
int function() {
return real_function(&global_bar);
}
Используя это, вы увеличиваете цепочку вызовов, извлекая зависимости и, надеюсь, однажды удаляете последний вызов варианта, который требует глобальные переменные. А пока вы можете писать тесты для функций, которые больше не зависят от глобальных вещей. Обратите внимание, что для C ++ вы должны использовать ссылки вместо указателей и, вероятно, передавать необходимые внешние объекты в конструктор класса. Это также называется внедрением зависимостей. Обязательно изучите этот термин для полного понимания.
Другой способ проверки функций, затрагивающих глобальные переменные, состоит в том, чтобы использовать функцию настройки теста, чтобы сбросить глобальное в известное состояние. Это все еще требует связывания в глобальном, хотя, что может оказаться трудным. А если не использовать глобальные переменные, это может значительно улучшить кодовую базу, поэтому принятие этого сообщения также приводит к неверному сообщению.
Ульрих Экхардт, по сути, говорит: «Вы должны избавиться от глобальных переменных, чтобы сделать легко тестируемый код». Но вы действительно должны идти дальше.
Для любой глобальной функции, которую вы хотите проверить, вы должны посмотреть на
Тогда подумайте:
Если ваша функция является функцией объекта, а не глобальной функцией, вы можете рассмотреть дополнительно:
Последнее, что я хотел бы рассмотреть, чтобы сделать функцию тестируемой, относится ли она к классу.
После того, как все это решено, вы можете легко смоделировать нужные биты. Если вы используете gtest, вы можете использовать gmock, чтобы сделать это проще. (Я использовал gmock с gtest и раньше, и он довольно безболезненный.)
(И да, я использовал этот подход на больших базах кода раньше … обычно довольно больно начинать с него, но как только вы привыкните к нему и код станет более тестируемым — все улучшится.)