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

使用Ceedling+CMock单元测试时出现Undefined reference错误求助

Ceedling/CMock单元测试出现"undefined reference"链接错误

我是Ceedling和CMock单元测试新手,编写基础测试代码后,无论是手动用gcc编译还是通过Ceedling执行测试,都在链接阶段出现一系列undefined reference to错误,指向CMock生成的期望函数。


手动编译命令与错误信息

最初使用的编译命令:

gcc ./test/test_shape_container.c ./src/shape_container.c ./src/rectangle.c Unity/src/unity.c -o test_shape_container.exe

抛出的链接错误:

undefined reference to `rectangle_init_CMockExpect'
undefined reference to `rectangle_init_CMockExpect'
undefined reference to `rectangle_get_area_CMockExpectAndReturn'
undefined reference to `rectangle_get_perimeter_CMockExpectAndReturn'
undefined reference to `rectangle_is_square_CMockExpectAndReturn'
undefined reference to `rectangle_init_CMockExpect'
undefined reference to `rectangle_get_area_CMockExpectAndReturn'
undefined reference to `rectangle_get_perimeter_CMockExpectAndReturn'
undefined reference to `rectangle_is_square_CMockExpectAndReturn'
collect2.exe: error: ld returned 1 exit status

调整后的编译命令仍报错:

gcc ./test/test_shape_container.c ./src/shape_container.c ./src/rectangle.c -o test_shape_container.exe

测试文件代码(test_shape_container.c)

#include "../Unity/src/unity.h"
#include "../src/shape_container.h"
#include "../mocks/mock_rectangle.h"

void setUp(void)
{
    // required by ceedling
}

void tearDown(void)
{
    // required by ceedling
}

void WinMain(void)
{
    // required
}

void test_shape_container_init(void)
{
    // Set up known values
    uint16_t length = 4;
    uint16_t width = 3;

    // State, in order of call, what expectations we have, and the expected values to be returned, if any
    rectangle_init_Expect(length, width);

    // Run Actual Function Under Test
    shape_container_init(length, width);
}

void test_shape_container_calc_rect_is_square(void)
{
    // Set up known values
    uint16_t length = 4;
    uint16_t width = 4;
    uint16_t x_area = length * width;
    uint16_t x_perimeter = length + width + length + width;
    bool x_is_square = true;

    // State, in order of call, what expectations we have, and the expected values to be returned, if any
    rectangle_init_Expect(length, width);
    rectangle_get_area_ExpectAndReturn(x_area);
    rectangle_get_perimeter_ExpectAndReturn(x_perimeter);
    rectangle_is_square_ExpectAndReturn(x_is_square);

    // Run Actual Function Under Test
    bool is_square = shape_container_calc_rect(length, width);

    // We can still verify whatever things we normally would after
    TEST_ASSERT_EQUAL(x_is_square, is_square);
}

void test_shape_container_calc_rect_is_not_square(void)
{
    // Set up known values
    uint16_t length = 4;
    uint16_t width = 3;
    uint16_t x_area = length * width;
    uint16_t x_perimeter = length + width + length + width;
    bool x_is_square = false;

    // State, in order of call, what expectations we have, and the expected values to be returned, if any
    rectangle_init_Expect(length, width);
    rectangle_get_area_ExpectAndReturn(x_area);
    rectangle_get_perimeter_ExpectAndReturn(x_perimeter);
    rectangle_is_square_ExpectAndReturn(x_is_square);

    // Run Actual Function Under Test
    bool is_square = shape_container_calc_rect(length, width);

    // We can still verify whatever things we normally would after
    TEST_ASSERT_EQUAL(x_is_square, is_square);
}

project.yml配置

---

# Notes:
# Sample project C code is not presently written to produce a release artifact.
# As such, release build options are disabled.
# This sample, therefore, only demonstrates running a collection of unit tests.

:project:
  :use_exceptions: FALSE
  :use_test_preprocessor: TRUE
  :use_auxiliary_dependencies: TRUE
  :build_root: build
#  :release_build: TRUE
  :test_file_prefix: test_
  :which_ceedling: gem
  :ceedling_version: 0.31.1
  :default_tasks:
    - test:all

#:test_build:
#  :use_assembly: TRUE

#:release_build:
#  :output: MyApp.out
#  :use_assembly: FALSE

:environment:

:extension:
  :executable: .out

:paths:
  :test:
    - +:test/**
    - -:test/support
  :source:
    - src/**
  :support:
    - test/support
  :libraries: []

:defines:
  # in order to add common defines:
  #  1) remove the trailing [] from the :common: section
  #  2) add entries to the :common: section (e.g. :test: has TEST defined)
  :common: &common_defines []
  :test:
    - *common_defines
    - TEST
  :test_preprocess:
    - *common_defines
    - TEST

:cmock:
  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: TRUE
  :plugins:
    - :ignore
    - :callback
  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8

# Add -gcov to the plugins list to make sure of the gcov plugin
# You will need to have gcov and gcovr both installed to make it work.
# For more information on these options, see docs in plugins/gcov
:gcov:
  :reports:
    - HtmlDetailed
  :gcovr:
    :html_medium_threshold: 75
    :html_high_threshold: 90

#:tools:
# Ceedling defaults to using gcc for compiling, linking, etc.
# As [:tools] is blank, gcc will be used (so long as it's in your system path)
# See documentation to configure a given toolchain for use

# LIBRARIES
# These libraries are automatically injected into the build process. Those specified as
# common will be used in all types of builds. Otherwise, libraries can be injected in just
# tests or releases. These options are MERGED with the options in supplemental yaml files.
:libraries:
  :placement: :end
  :flag: "-l${1}"
  :path_flag: "-L ${1}"
  :system: []    # for example, you might list 'm' to grab the math library
  :test: []
  :release: []

:plugins:
  :load_paths:
    - "#{Ceedling.load_path}"
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
...

已尝试操作

  • 曾尝试将mock_rectangle.c加入编译,但引发其他问题;
  • 调整编译命令后仍抛出未定义引用错误;
  • Ceedling执行测试也在编译链接阶段失败。

解决方案

1. 优先使用Ceedling自动构建,避免手动gcc编译

Ceedling会自动处理CMock生成的mock文件、依赖管理、头文件路径和编译链接逻辑,手动编译极易遗漏必要组件。执行以下命令运行测试:

# 运行所有测试
ceedling test:all

# 单独运行shape_container测试
ceedling test:shape_container

2. 检查CMock生成的mock文件

Ceedling会自动在build/test/mocks目录下生成mock_rectangle.c和mock_rectangle.h,确保这些文件已生成。如果未生成,先清理构建目录再重新构建:

ceedling clean
ceedling test:all

3. 手动编译时的正确姿势

如果必须手动编译,不要链接真实的rectangle.c(mock就是用来替代真实实现的),需要包含mock文件、Unity、被测试文件和测试文件,并指定头文件路径:

gcc ./test/test_shape_container.c ./src/shape_container.c ./build/test/mocks/mock_rectangle.c ./Unity/src/unity.c -I./Unity/src -I./src -I./build/test/mocks -o test_shape_container.exe

4. 排查加入mock后的冲突问题

如果加入mock_rectangle.c后出现符号重复定义错误,说明同时链接了真实的rectangle.c和mock文件,必须移除真实的源文件。若出现头文件重复包含问题,确保所有头文件都添加了包含保护:

#ifndef RECTANGLE_H
#define RECTANGLE_H
// 头文件内容
#endif

5. 确认project.yml配置正确性

  • 确保:paths中的:source包含src/**,:test包含test/**,Ceedling能正确找到所有文件;
  • 确认:cmock下的:mock_prefix为mock_,与你的mock文件命名一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:34:53