You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用cmocka对C语言数据库函数做单元测试及包装SQL API?

使用CMocka为C语言数据库函数编写单元测试

CMocka完全可以用来测试你的数据库函数,它的函数打桩(wrap)机制能轻松隔离外部SQL API依赖,下面是核心场景的测试示例及dbclose函数的wrap配置方案。


一、核心场景测试示例

1. 数据库连接测试

假设你的连接函数dbconnect依赖SQLAllocConnect、SQLDriverConnect等API,测试用例如下:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "Base.h"

// Mock SQL API函数
int __wrap_SQLAllocConnect(SQLHENV henv, SQLHDBC *phdbc) {
    *phdbc = (SQLHDBC)0xDEADBEEF; // 返回模拟连接句柄
    return SQL_SUCCESS;
}

int __wrap_SQLDriverConnect(SQLHDBC hdbc, SQLHWND hwnd, SQLCHAR *szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR *szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT *pcbConnStrOut, SQLUSMALLINT fDriverCompletion) {
    check_expected(hdbc); // 验证传入句柄是否符合预期
    return SQL_SUCCESS;
}

void test_dbconnect_success(void **state) {
    (void)state;

    // 设置期望:调用SQLDriverConnect时传入指定句柄
    expect_value(__wrap_SQLDriverConnect, hdbc, (SQLHDBC)0xDEADBEEF);

    int result = dbconnect(0); // 调用你的连接函数
    assert_int_equal(result, 0); // 假设成功返回0
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test_dbconnect_success),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}

2. 数据库读写/更新测试

假设查询函数dbquery依赖SQLExecDirect、SQLFetch等API,测试用例如下:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "Base.h"

// Mock SQLExecDirect
int __wrap_SQLExecDirect(SQLHSTMT hstmt, SQLCHAR *szSqlStr, SQLINTEGER cbSqlStr) {
    check_expected(szSqlStr); // 验证执行的SQL语句
    return SQL_SUCCESS;
}

// Mock SQLFetch:第一次返回数据,第二次返回无数据
int __wrap_SQLFetch(SQLHSTMT hstmt) {
    static int call_count = 0;
    return (call_count++ == 0) ? SQL_SUCCESS : SQL_NO_DATA;
}

void test_dbquery_fetch_data(void **state) {
    (void)state;

    // 设置期望:执行指定SQL语句
    expect_string(__wrap_SQLExecDirect, szSqlStr, "SELECT * FROM users WHERE id=1");

    char buffer[256];
    int result = dbquery(0, "SELECT * FROM users WHERE id=1", buffer);
    assert_int_equal(result, 0); // 假设成功返回0
    assert_string_equal(buffer, "mock_user_data"); // 验证返回的模拟数据
}

二、dbclose函数的Wrap配置及SQL API单独包装

1. CMakeLists.txt配置

要包装SQLDisconnect和SQLFreeConnect,需在CMake中指定这两个函数为wrap目标,同时链接CMocka库:

cmake_minimum_required(VERSION 3.10)
project(db_unit_tests)

# 引入CMocka库
find_package(CMocka REQUIRED)

# 编译测试程序:包含你的数据库实现文件和测试文件
add_executable(db_tests Tests.c Base.c)

# 链接CMocka依赖
target_link_libraries(db_tests PRIVATE ${CMOCKA_LIBRARIES})

# 指定需要Wrap的SQL API函数
set_target_properties(db_tests PROPERTIES
    WRAP_FUNCTIONS "SQLDisconnect;SQLFreeConnect"
)

# 包含头文件目录
target_include_directories(db_tests PRIVATE ${CMOCKA_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR})

2. Tests.c中使用Mock函数

在测试文件中定义__wrap_SQLDisconnect和__wrap_SQLFreeConnect替代原API,并设置调用期望:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "Base.h"

// 模拟Base.c中的静态hDbc数组
SQLHDBC hDbc[10] = {NULL};

// Mock SQLDisconnect
int __wrap_SQLDisconnect(SQLHDBC hdbc) {
    check_expected(hdbc); // 验证传入句柄
    return SQL_SUCCESS;
}

// Mock SQLFreeConnect
int __wrap_SQLFreeConnect(SQLHDBC hdbc) {
    check_expected(hdbc); // 验证传入句柄
    return SQL_SUCCESS;
}

void test_dbclose_valid_handle(void **state) {
    (void)state;

    // 设置模拟连接句柄
    hDbc[0] = (SQLHDBC)0x12345678;

    // 设置调用期望
    expect_value(__wrap_SQLDisconnect, hdbc, (SQLHDBC)0x12345678);
    expect_value(__wrap_SQLFreeConnect, hdbc, (SQLHDBC)0x12345678);

    dbclose(0); // 调用dbclose

    // 验证句柄被置空
    assert_null(hDbc[0]);
}

void test_dbclose_null_handle(void **state) {
    (void)state;

    hDbc[0] = NULL;
    dbclose(0); // 调用dbclose,此时不会触发SQL API调用

    assert_null(hDbc[0]);
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test_dbclose_valid_handle),
        cmocka_unit_test(test_dbclose_null_handle),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}

3. 单独包装SQL API函数的可行性

完全可以单独包装SQLDisconnect和SQLFreeConnect。CMocka通过动态符号替换实现wrap,只要在CMake的WRAP_FUNCTIONS中列出目标函数名,测试时就会用你定义的__wrap_xxx函数替代原API,无需修改原业务代码。

注意:如果原SQL API是通过静态库链接的,需确保编译时添加-fPIC参数,避免静态符号重定位问题。


内容的提问来源于stack exchange,提问作者jst

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 00:46:09