Консультации по системным вызовам

Я вижу, что это старый пост, но я нашел его полезным и подумал, что поделился бы решением, аналогичным тому, что предложил @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);
}
30
задан Robert S. Barnes 27 May 2010 в 19:58
поделиться

4 ответа

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;
10
ответ дан 27 November 2019 в 23:54
поделиться

В этом случае вам не нужно имитировать getaddrinfo , скорее, вам нужно протестировать, не полагаясь на его функциональность. У Патрика и Ноя есть хорошие аргументы, но у вас есть как минимум два других варианта:

Вариант 1: Подкласс для тестирования

Поскольку у вас уже есть ваш объект в классе, вы можете создать подкласс для тестирования. Например, предположим, что это ваш настоящий класс:

class DnsClass {
    int lookup(...);
};

int DnsClass::lookup(...) {
    return getaddrinfo(...);
}

Затем для тестирования вы должны создать подкласс следующим образом:

class FailingDnsClass {
    int lookup(...) { return 42; }
};

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

ПРИМЕЧАНИЕ. Это очень похоже на ответ Патрика, но (надеюсь) не связано с изменением производственного кода, если вы еще не настроили внедрение зависимостей.

Вариант 2: Используйте стык ссылок

В 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
27
ответ дан 27 November 2019 в 23:54
поделиться

Поищите шаблоны для "Dependency Injection".

Dependency Injection работает следующим образом: вместо вызова getaddrinfo непосредственно в вашем коде, код использует интерфейс, который имеет виртуальный метод "getaddrinfo".

В реальном коде вызывающая сторона передает реализацию интерфейса, которая отображает виртуальный метод "getaddrinfo" интерфейса на реальную функцию ::getaddrinfo.

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

EDIT: Прочитайте "Эффективная работа с устаревшим кодом" Майкла Фезерса для получения дополнительных советов.

13
ответ дан 27 November 2019 в 23:54
поделиться

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

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

Дело в том, чтобы хранить такие вещи, которые нельзя протестировать, завернутые во что-то настолько тривиальное, что на самом деле не требует тестирования, а затем имитировать эту оболочку для тестирования более сложных взаимодействий. Поцелуй здесь особенно важен.

1
ответ дан 27 November 2019 в 23:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: