如何从C共享库中模拟几个函数?

oaxa6hgo  于 2023-02-03  发布在  其他
关注(0)|答案(2)|浏览(92)

我正在第三方封闭源代码C库上开发Erlang NIF Package 器。NIF Package 器本质上是一个用C编写的Erlang模块,它调用来自第三方C库的函数。我想在测试中模拟一些来自该库的调用。库本身至少有100个函数,但目前我只想模拟2或3个。测试由rebar3工具执行。所以没有使用C测试框架。2有相对简单的方法吗?
我查找了一些模拟解决方案,但它们要么依赖于在模拟库中将导出的符号标记为“弱”(我无法修改),我使用了一个成熟的测试框架(我不能使用它,因为测试中的API是Erlang)或者甚至没有编译(我正在Ubuntu Linux上开发)。我还尝试了一个简单的解决方案,即构建一个只包含模拟函数的共享库,并在加载第三方库之前加载该函数(使用LD_PRELOAD),但是没有成功,我得到了undefined symbol错误,因为一个符号只在原始库中,而不在我的模拟代码中。你有其他的想法吗?

eh57zj3b

eh57zj3b1#

最简单、可靠和可读的解决方案就是添加另一层间接性。

// sal - System Abstraction Layer
// sal/stdlib.{c,h}
#if MOCK_INTERFACE
int sal_system(const char *command) {
    return -1;
}
// other functions...
#else
// in the header for full performance
// or use static functions.
#define sal_system system
#endif

例如,在整个代码中使用sal_system()而不是system()。如果使用定义的宏MOCK_INTERFACE编译项目,则将使用模拟接口。

bd1hkmkf

bd1hkmkf2#

下面描述了每个单元测试中带有粒度控制的上述间接方法的通用版本:https://ricomariani.medium.com/a-very-simple-mocking-strategy-for-straight-c-7e4b357d90e4(感谢您在ricomariani网站上发表此技术!)
通过使用所描述的宏,您可以将函数调用定向到函数指针,而函数指针可以是单元测试中模拟实现的指针。同时,如果禁用标记,则开销为0。因此,最终发布版本不会受到任何影响。

#ifdef C_MOCKS_ENABLED

#define C_MOCK_DEF(context, api) __typeof(__typeof(api) *) mockptr_##context##api = &api;
#define C_MOCK_REF(context, api) extern __typeof(__typeof(api) *) mockptr_##context##api;
#define C_MOCK_SET(context, api, mockapi) (mockptr_##context##api = &mockapi)
#define C_MOCK_RESET(context, api) (mockptr_##context##api = &api)
#define C_MOCK_USE(context, api) (*mockptr_##context##api)

#else

#define C_MOCK_DEF(context, api)
#define C_MOCK_REF(context, api)
#define C_MOCK_SET(context, api, mockapi)
#define C_MOCK_RESET(context, api)
#define C_MOCK_USE(context, api) api

#endif

示例用法(对上述链接中的示例稍作调整):

// ---------- subject.h
const char *GetText(void);

// ---------- subject.c
#include "subject.h"

// create function pointer
C_MOCK_DEF(subject, return_something);
// redefine function call to above pointer
#define return_something C_MOCK_USE(subject, return_something)

const char *GetText() {
  return return_something();
}

// ---------- subject_test.c

#include "subject.h"

// extern reference
C_MOCK_REF(subject, return_something);

// mock impl
static const char *mock_return_something() {
  return "test text";
}

void GetText_Test()
{
  // point ptr to mock impl
  C_MOCK_SET(subject, return_something, mock_return_something);

  // run function under test
  const char *txt = GetText();
  assert(strcmp(txt, "test text") == 0);

  // reset ptr
  C_MOCK_RESET(subject, puts);
}

相关问题