Trên hệ thống ELF, bạn có thể sử dụng elf_hook để tạm thời thay thế các phiên bản thực của các chức năng khác nhau bằng các phiên bản được chế bản của riêng bạn.
Nó cho phép bạn chuyển hướng cuộc gọi đến bất kỳ chức năng nào từ bên trong một thư viện được chia sẻ đến chức năng tùy ý của riêng bạn.
- Tạo một thư viện chia sẻ chứa mã theo thử nghiệm
- Trong thử nghiệm của bạn tải các thư viện chia sẻ tự động (
dlopen
)
- Chuyển những biểu tượng bạn muốn thử chức năng thử nghiệm của bạn (
elf_hook
)
- Bây giờ mọi cuộc gọi đến chức năng thực sự trong thư viện (mã đang được kiểm tra) sẽ được chuyển hướng đến chức năng được mô phỏng của bạn
Một điểm cộng cho phương pháp này là bạn ca n vẫn gọi hàm ban đầu khi được yêu cầu.
- Nếu đối với một số thử nghiệm bạn muốn thực hiện cuộc gọi, ví dụ:
getaddrinfo
, thành công, bạn có thể gọi phiên bản hệ thống.
- Trong các thử nghiệm khác, bạn có thể sử dụng phiên bản giả mạo của riêng bạn, ví dụ:
mocked_getaddrinfo
và trả lại bất kỳ thứ gì bạn muốn.
- Bạn có thể tạo bao nhiêu
mocked_getaddrinfo
chức năng như bạn muốn, để thử nghiệm nhiều kịch bản
elf_hook có chữ ký sau đây:
void* elf_hook(char const* library_filename,
void const* library_address,
char const* function_name,
void const* substitution_address);
Bạn sẽ sử dụng nó như thế này:
#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;
}
Mọi cuộc gọi đến getaddrinfo
từ trong thư viện đang được kiểm tra sẽ gọi mocked_getaddrinfo
.
Bài viết toàn diện của tác giả elf_hook, Anthony Shoumikhin, là here.
(không phải là bản sao, nhưng điều này có thể hữu ích?) Http://stackoverflow.com/questions/2924440/advice-on-mocking-system-calls – IdeaHat
Có phải tôi, hoặc là những hành vi bạn đang cố gắng kiểm tra bên ngoài phạm vi kiểm tra đơn vị và thử nghiệm tích hợp? Một điểm giả tưởng, tôi cho rằng, nhưng có thể thay đổi phạm vi câu trả lời. Đó là hai chủ đề rất khác nhau đối với hầu hết các nhà phát triển. – ChrisCM
@ChrisCM Theo tôi, đây không phải là thử nghiệm tích hợp, vì tôi không muốn triển khai thực sự hoặc sử dụng nhiều máy. Ngoài ra, tôi không muốn kiểm tra toàn bộ sản phẩm, nhưng chỉ một số phần thực hiện như vòng lặp sự kiện, dịch vụ mạng, phát trực tuyến. Và nó phải là một nhị phân đơn, làm tất cả công việc. Liệu nó trông giống như thử nghiệm tích hợp theo ý kiến của bạn? –