2014-12-03 30 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

相關問題