W systemach ELF można użyć elf_hook do tymczasowej zamiany rzeczywistych wersji różnych funkcji na własne, wyśmiewane wersje.
Umożliwia przekierowywanie połączeń do dowolnej funkcji z poziomu biblioteki współużytkowanej do własnej, arbitralnej funkcji.
- Tworzenie wspólnej biblioteki zawierającej kod badanego
- W swojej próbie załadowania biblioteki współdzielonej dynamicznie (
dlopen
)
- przekierować symbole chcesz szydzić do funkcji testowych (
elf_hook
)
- teraz wszelkie wywołania do rzeczywistej funkcji w bibliotece (testowany kod) zostaną przekierowane do twojej wyśmiewanej funkcji:
Dodatkową zaletą tej metody jest to, że masz ca n nadal wywołuje oryginalną funkcję, gdy jest to wymagane.
- Jeśli dla niektórych testów chcesz mieć połączenie, np.
getaddrinfo
, powodzenie, możesz zadzwonić do wersji systemu.
- W innych testach możesz użyć własnej, wyśmiewanej wersji, np.
mocked_getaddrinfo
, i zwrócić, co chcesz.
- Można utworzyć wiele
mocked_getaddrinfo
funkcje, jak chcesz, aby przetestować wiele scenariuszy
elf_hook ma następujący podpis:
void* elf_hook(char const* library_filename,
void const* library_address,
char const* function_name,
void const* substitution_address);
będzie go używać tak:
#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;
}
Wszelkie połączenia z getaddrinfo
z testowanej biblioteki będą teraz wywoływać numer mocked_getaddrinfo
.
Obszerny artykuł autorstwa autora elf_hook, Anthony'ego Shoumikhina, to here.
(nie duplikat, ale może to być pomocne?) Http://stackoverflow.com/questions/2924440/advice-on-mocking-system-calls – IdeaHat
Czy to ja, czy też zachowania, które próbujesz test poza zakresem testowania jednostkowego i testami integracyjnymi? Pewnie pedantyczny punkt, ale mogę zmienić zakres odpowiedzi. To są dwa bardzo różne tematy dla większości programistów. – ChrisCM
@ChrisCM Moim zdaniem nie jest to test integracyjny, ponieważ nie chcę wykonywać rzeczywistych wdrożeń lub używać więcej niż jednego komputera. Ponadto nie chcę testować całego produktu, ale tylko niektóre elementy implementacyjne, takie jak pętla zdarzeń, usługa sieciowa, transmisja strumieniowa. I powinien to być pojedynczy plik binarny, który wykonuje całą pracę. Czy według ciebie wygląda testowanie integracyjne? –