2014-12-03 83 views
12

我想用单元测试来覆盖我的代码。这是好事。但我有一个问题 - 我有一个网络代码。该代码确实从主机名解析IPv4和IPv6地址,绑定到接口,监听,连接等。在Linux上测试网络代码的最准确的方法是什么?

我假设存在一些C/C++测试框架,可以部署在几乎任何工作站或某种编程技术上,允许我:

  • 建立和拆除与IPv4的自定义网络接口和IPv6地址
  • 模拟不同干扰的行为,像丢失数据包,timeouting,连接掉落等
  • 绑定主机名接口和解决他们。

主要目标不是与机器上的真实网络接口交互或混乱。


你有什么建议吗?

+3

(不是重复,但这可能有帮助吗?)http://stackoverflow.com/questions/2924440/advice-on-mocking-system-calls – IdeaHat 2014-12-03 18:32:41

+0

是我还是你想要的行为在单元测试范围之外进行测试,并进行集成测试?我想,这是一个迂腐点,但可以改变答案的范围。对大多数开发者来说,这是两个非常不同的话题 – ChrisCM 2014-12-03 18:38:56

+0

@ChrisCM在我看来,这不是一个集成测试,因为我不想做一个真正的部署,或者使用多台机器。此外,我不想测试整个产品,但只有一些实现部分,如事件循环,网络服务,流媒体。它应该是一个单一的二进制文件,完成所有的工作。你认为它看起来像集成测试吗? – 2014-12-03 18:46:33

回答

0

如果你愿意花时间和精力去做,你可以单元测试任何代码。基本上,通过单元测试,您有兴趣实现目标代码覆盖率以及某些行业MC/DC覆盖率。在某些情况下,您需要编写模拟代码(模块将导出类似OS API /套接字API的函数导出到您的待测试单元),然后这些代码将帮助驱动程序执行“正在测试的单元”中的每个角落.c/.cpp文件)通过返回您告诉它的值。

您可能需要为测试应用程序的其余部分指定不同的包含路径以避免名称冲突,并且您还可能必须在测试头中使用预处理器宏以使模拟API出现像真正的交易到你的“单位”,并保持与生产代码相同。

您可以测试硬件驱动程序和任何类型的低级代码。例如,如果您的代码正在写入和读取内存映射寄存器,您希望基于FPGA的逻辑发生变化,并且您没有硬件(或者在没有实际旅行的情况下生成测试条件非常困难到mars),那么你可以编写宏/包装函数读取和写入寄存器,将返回你的嘲弄值。过去曾使用过CppUTest,很容易学习。我想谷歌搜索会带来很多结果。

+2

你应该专注于解决实际问题......这只是对如何进行测试的一般说明 - 几乎所有的测试 – 2014-12-03 23:04:47

4

在ELF系统上,您可以使用elf_hook用您自己的模拟版本临时替换各种功能的实际版本。

它允许您将调用从共享库内的任何函数重定向到您自己的任意函数。

  • 创建一个包含测试
  • 在您的测试代码共享库加载共享库动态(dlopen
  • 重定向要嘲笑你的测试功能(elf_hook
  • 现在符号任何对库内实际函数的调用(被测代码)都将被重定向到您的模拟函数

此方法的一个优点是您可以在需要时仍然会调用原始功能。

  • 如果对于某些需要呼叫的测试,例如getaddrinfo,您可以调用系统版本。
  • 在其他测试中,您可以使用自己的模拟版本,例如mocked_getaddrinfo,并让它返回任何你想要的。
  • 只要你想你可以创建许多mocked_getaddrinfo功能,测试多种场景

elf_hook具有以下特征:

void* elf_hook(char const* library_filename, 
       void const* library_address, 
       char const* function_name, 
       void const* substitution_address); 

你会使用这样的:

#include <dlfcn.h> 
#include "elf_hook.h" 

void do_stuff(); // from the library under test (do_stuff calls getaddrinfo) 

// our mocked function which will alter the behaviour inside do_stuff() 
int mocked_getaddrinfo(const char* node, 
         const char* service, 
         const struct addrinfo* hints, 
         struct addrinfo** res) 
{ 
    // return a broken value to test a getaddrinfo failure 
    return 42; 
} 

// another version which actually calls the real function 
int real_getaddrinfo(const char* node, 
        const char* service, 
        const struct addrinfo* hints, 
        struct addrinfo** res) 
{ 
    // the real getaddrinfo is available to us here, we only replace it in the shared lib 
    return getaddrinfo(node, service, hints, res); 
} 

int main() 
{ 
    const char* lib_path = "path/to/library/under/test.so"; 

    // load the library under test 
    void* lib_handle = dlopen(lib_path, RTLD_LAZY); 

    // test 1: getraddrinfo is broken 
    //-------------------------------- 
    // replace getaddrinfo with our 'mocked_getaddrinfo' version 
    elf_hook(lib_path, LIBRARY_ADDRESS_BY_HANDLE(lib_handle), 
      "getaddrinfo", mocked_getaddrinfo); 

    // call a function in the library under test where getaddrinfo fails 
    do_stuff(); 

    // test 2: getraddrinfo is the system version 
    //-------------------------------- 
    // replace getaddrinfo with our 'real_getaddrinfo' version 
    elf_hook(lib_path, LIBRARY_ADDRESS_BY_HANDLE(lib_handle), 
      "getaddrinfo", real_getaddrinfo); 

    // call the same function in the library, now getaddrinfo works 
    do_stuff(); 

    dlclose(lib_handle); 
    return 0; 
} 

从被测库中调用getaddrinfo现在将调用mocked_getaddrinfo

elf_hook作者Anthony Shoumikhin的综合文章是here

相关问题