2013-03-06 3 views
2

Я начинаю использовать CppUTest для некоторых проектов на C/C++. Особенно смешное расширение звука хорошее, но я в настоящее время борется, как правильно настроить насмешки. Предположите класс низкого уровня для абстрагирования сетевого сокета.CppUTest: как передать больше данных на определенный макет?

Мой первый метод:

 
size_t CMockSocket::recv(void* buf, size_t len) 
{ 
    return (size_t) mock().actualCall("recv") 
     .withParameter("len", (int) len) 
     .returnValue().getIntValue(); 
} 

Настройка ожидания:

 
mock().expectOneCall("recv") 
    .withParameter("len", 20) 
    .andReturnValue(15); 

До сих пор так хорошо. Но мне нужно будет предоставить больше данных - в этом случае 15 байт, которые я хочу вернуть с помощью указателя buf. Я попытался использовать методы .setData() и setDataObject(). Но похоже, что эти функции хранят данные в именованных переменных, а не с ожидаемым вызовом функции. Поэтому последующие вызовы будут отменять предыдущие данные, правильно?

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

 
class CRecvResults 
{ 
public: 
    size_t m_returnValue; 
    void* m_buffer; 
    CRecvResults(size_t returnValue, void* buffer) : 
    m_returnValue(returnValue), m_buffer(buffer) {} 
    ~CRecvResults() {} 
}; 

size_t CMockSocket::recv(void* buf, size_t len) 
{ 
    CRecvResults* pResults = 
     (CRecvResults*) mock().actualCall("recv") 
     .withParameter("len", (int) len) 
     .returnValue().getPointerValue()); 

    assert(pResults != NULL); 
    assert(buf != NULL); 
    assert(len >= pResults->m_buffer.length()); 
    memcpy(buf, pResults->m_buffer.data(), pResults->m_buffer.length()); 
    return pResults->m_returnValue; 
} 

И ожидание:

 
char* buffer = "some returning buffer content at least 15 chars long"; 
CRecvResults results(15, buffer); 
mock().expectOneCall("recv") 
     .withParameter("len", 20) 
     .andReturnValue(&results); 

Вопрос: это правильный путь или я что-то пропустила? Мне нужен буферный контент, потому что мне нужно протестировать интерпретатор результатов.

Мой второй метод:

 
bool CMockSocket::send(const void* buf, size_t len) 
{ 
    return mock().actualCall("send") 
     .withParameter("len", (int) len) 
     .returnValue().getIntValue(); 
} 

Настройка ожидания:

 
mock().expectOneCall("send") 
    .withParameter("len", 20) 
    .andReturnValue(true); 

В этом случае я хочу, чтобы проверить исходящие данные в BUF, сгенерированные кодом испытуемой. Поэтому мне нужно вернуть содержимое буфера (и длину) в тестовую функцию. Как это:

 
class CSendResults 
{ 
public: 
    bool m_returnValue; 
    char* m_buffer; 
    size_t m_length; 
    CSendResults(bool returnValue, char* buffer, size_t length) : 
    m_returnValue(returnValue), m_buffer(buffer), m_length(length) {} 
    ~CSendResults() {} 
}; 

bool CMockSocket::send(const void* buf, size_t len) 
{ 
    CSendResults* pResults = 
     (CSendResults*) mock().actualCall("send") 
     .returnValue().getPointerValue(); 

    assert(pResults != NULL); 
    assert(buf != NULL); 
    assert(pResults->m_length >= len); 
    memcpy(pResults->m_buffer, buf, len); 
    pResults->m_length = len; 
    return pResults->m_returnValue; 
} 

И ожидание:

 
char buffer[1024]; 
CSendResults results(true, buffer, sizeof(buffer); 
mock().expectOneCall("send") 
     .withParameter("len", 20) 
     .andReturnValue(&results); 

// further checks for results content... 

Это выглядит очень некрасиво и с большим количеством накладных расходов для меня. Опять мой вопрос: это правильный путь или мне что-то не хватает?

И мой последний вопрос: может быть, я тестирую совершенно неправильный путь? Пожалуйста, дайте мне знать о вашем опыте и практике!

ответ

2

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

{ 
    return (size_t) mock().actualCall("recv") 
     .withOutputParameter("buf", buf) 
     .withParameter("len", (int) len) 
     .returnUnsignedLongIntValueOrDefault(0); 
} 

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

char buffer[] = "blabla"; 

mock().expectOneCall("recv") 
    .withOutputParameterReturning("buf", buffer, sizeof(buffer)) 
    .withParameter("len", sizeof(buffer)) 
    .andReturnValue(sizeof(buffer)); 

Содержимое buffer будет скопирован вызвавшей издеваться через выходной параметр. Поэтому уродливое обходное решение больше не требуется.

+0

Thats хорошие новости! Я скоро проверю это, выгляжу как чистое решение. – Andi

+0

BTW, в какой версии эта функция добавлена? – Andi

+0

Он представлен в версии 3.6. С тех пор было много улучшений, поэтому стоит взглянуть на нынешнюю голову. –

0

Я использую следующее уродливое обходное решение. Я определяю тип структуры для возвращаемого значения плюс дополнительные данные, которые мне нужны в моей поддельной функции. Я выделяю и заполняю один экземпляр, где я вызываю expectOneCall() и передаю его с помощью .andReturnValue() ". В поддельной функции я получаю свои данные с помощью «... returnValue(). GetPointerValue()», тогда я устанавливаю возвращаемое значение struct-> rc и обрабатываю свои дополнительные данные (копируйте/сравнивайте их с буфером). Перед выходом из поддельной функции структура освобождается.

Таким образом, дополнительные данные привязаны к списку параметров вызова функции.

Foltos

+0

Поблагодарите Foltos за ваш комментарий. Я вижу, что у нас есть аналогичные подходы с использованием настраиваемой структуры/объекта. С большим опытом работы в прошлом месяце у меня сложилось впечатление, что этот сценарий не будет возникать очень часто. Я попытался понять, как другие решения xUnit справляются с этой проблемой, но я не нашел более общих решений. – Andi