Я вижу, что это старый пост, но я нашел его полезным и подумал, что поделился бы решением, аналогичным тому, что предложил @kzqai:
У меня есть функция, которая получает два параметра, такие как ...
function getTableInfo($inTableName, $inColumnName) {
....
}
Внутри я проверяю массивы, которые я установил, чтобы убедиться, что доступны только таблицы и столбцы с «блаженными» таблицами:
$allowed_tables_array = array('tblTheTable');
$allowed_columns_array['tblTheTable'] = array('the_col_to_check');
Затем проверка PHP перед работающий PDO выглядит как ...
if(in_array($inTableName, $allowed_tables_array) && in_array($inColumnName,$allowed_columns_array[$inTableName]))
{
$sql = "SELECT $inColumnName AS columnInfo
FROM $inTableName";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
3 варианта
1 . Используйте насмешливые способности gnu linker, опцию - wrap
. Я никогда не использовал это для тестирования производственного кода, так как я не узнал об этом, пока наша команда разработчиков не перешла к методу 3. Хотелось бы, чтобы мы нашли это раньше
ld --wrap=getaddrinfo /*the rest of the link line*/
or
g++ -Wl,--wrap=getaddrinfo /* the rest of the build line*/
// this in the unit tests.
bool g_getaddrinfo_use_real = true;
int g_getaddrinfo_ret = -1;
int g_getaddrinfo_errno = something;
int __wrap_getaddrinfo( const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res )
{
if( g_getaddrinfo_use_real )
return __real_getaddrinfo(node,service,hints,res);
errno = g_getaddrinfo_errno;
return g_getaddrinfo_ret;
}
2 . Определите свой собственный getaddrinfo и статически свяжите его с тестовым приложением. Это будет работать только в том случае, если libc связана динамически, что верно в 99% случаев. Недостатком этого метода также является постоянное отключение реального getaddrinfo в приложении для модульного тестирования, но его невероятно просто реализовать.
int g_getadderinfo_ret = -1;
int g_getaddrinfo_errno = something;
int getaddrinfo( const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res )
{
errno = g_getaddrinfo_errno
return g_getaddrinfo_ret;
}
3 . Определите свою собственную посредническую функцию с тем же именем. Тогда еще можно называть оригинал, если хотите. Это намного проще с некоторыми макросами, которые помогают с повторением. Также вам придется использовать расширения GNU, если вы хотите имитировать вариативные функции ( printf
, open
и т. Д.).
typedef (*getaddrinfo_func_type)( const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res );
getaddrinfo_func_type g_getaddrinfo_func;
int getaddrinfo( const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res )
{
return g_getaddrinfo_func( node, service, hints, res )
}
int g_mock_getadderinfo_ret = -1;
int g_mock_getaddrinfo_errno = something;
int mock_getaddrinfo( const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res )
{
errno = g_mock_getaddrinfo_errno;
return g_mock_getaddrinfo_ret;
}
// use the original
g_getaddrinfo_func = dlsym(RTDL_NEXT, "getaddrinfo");
// use the mock version
g_getaddrinfo_func = &mock_getaddrinfo;
В этом случае вам не нужно имитировать getaddrinfo
, скорее, вам нужно протестировать, не полагаясь на его функциональность. У Патрика и Ноя есть хорошие аргументы, но у вас есть как минимум два других варианта:
Поскольку у вас уже есть ваш объект в классе, вы можете создать подкласс для тестирования. Например, предположим, что это ваш настоящий класс:
class DnsClass {
int lookup(...);
};
int DnsClass::lookup(...) {
return getaddrinfo(...);
}
Затем для тестирования вы должны создать подкласс следующим образом:
class FailingDnsClass {
int lookup(...) { return 42; }
};
Теперь вы можете использовать подкласс FailingDnsClass
для генерации ошибок, но при этом проверять, что все работает. правильно при возникновении состояния ошибки. В этом случае вам часто помогает внедрение зависимостей.
ПРИМЕЧАНИЕ. Это очень похоже на ответ Патрика, но (надеюсь) не связано с изменением производственного кода, если вы еще не настроили внедрение зависимостей.
В C ++ у вас также есть стыки времени компоновки, которые Майкл Фезерс описывает в Эффективная работа с устаревшим кодом .
Основная идея - использовать компоновщик и вашу систему сборки. При компиляции модульных тестов добавьте ссылку на вашу собственную версию getaddrinfo
, которая будет иметь приоритет над системной версией. Например:
test.cpp:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <iostream>
int main(void)
{
int retval = getaddrinfo(NULL, NULL, NULL, NULL);
std::cout << "RV:" << retval << std::endl;
return retval;
}
lib.cpp:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res
)
{
return 42;
}
А затем для тестирования:
$ g++ test.cpp lib.cpp -o test
$ ./test
RV:42
Поищите шаблоны для "Dependency Injection".
Dependency Injection работает следующим образом: вместо вызова getaddrinfo непосредственно в вашем коде, код использует интерфейс, который имеет виртуальный метод "getaddrinfo".
В реальном коде вызывающая сторона передает реализацию интерфейса, которая отображает виртуальный метод "getaddrinfo" интерфейса на реальную функцию ::getaddrinfo.
В модульных тестах вызывающая сторона передает реализацию, которая может имитировать сбои, тестировать условия ошибок, ... короче говоря: высмеивайте все, что хотите высмеять.
EDIT: Прочитайте "Эффективная работа с устаревшим кодом" Майкла Фезерса для получения дополнительных советов.
Хотя технически возможно, я не думаю, что это осуществимо. Вам нужно будет заменить реализацию этой функции, и вы, вероятно, не сможете и по-прежнему будете ссылаться на стандартную библиотеку в своей системе.
Вам следует позвонить посреднику. Затем вы можете издеваться над посредником во время тестирования и просто перейти к фактической функции в производстве. Вы можете даже подумать о создании класса, который взаимодействует с этой функцией и другими подобными ей функциями и предоставляет более общий интерфейс для вашей программы. Этот класс на самом деле не будет делать ничего, кроме переадресации вызовов большую часть времени, но во время тестирования его можно эффективно смоделировать, и вы можете протестировать все, что его использует.
Дело в том, чтобы хранить такие вещи, которые нельзя протестировать, завернутые во что-то настолько тривиальное, что на самом деле не требует тестирования, а затем имитировать эту оболочку для тестирования более сложных взаимодействий. Поцелуй здесь особенно важен.