使用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
相关产品推荐
相关产品推荐

