如何使用Ceedling测试含静态全局变量的函数并实现全面测试覆盖?
Testing functions that depend on static global variables can feel like a headache at first—since those variables are locked to their source file, your test code can't normally access them. But don't worry, Ceedling (built on Unity and CMock) has a couple of clean ways to handle this so you can test every possible value of global_var and hit full coverage.
Approach 1: Expose the Variable for Testing with Preprocessor Macros
This is the most straightforward method if you don't mind a tiny, non-intrusive tweak to your production code. The idea is to conditionally remove the static qualifier when building for tests, turning the variable into a global that your test code can modify.
Step 1: Modify the Source File (file_under_test.c)
Wrap the static variable in a preprocessor check for a test-specific define:
#ifdef TESTING int global_var; // Global when testing #else static int global_var; // Static in production #endif int func_under_test(){ switch(global_var){ case X: return SOME_VALUE; case Y: return SOME_OTHER_VALUE; // ... other cases default: return DEFAULT_VALUE; } }
Step 2: Configure Ceedling to Define TESTING
Add the TESTING define to your project.yml so it's automatically set when building tests:
:test_build: :compile_definitions: - TESTING
Step 3: Write Your Test Cases (test_file_under_test.c)
Now you can directly access and modify global_var in your tests:
#include "unity.h" #include "file_under_test.h" // Declare the variable (since it's global in test builds) extern int global_var; void setUp(void) { // Reset the variable before each test to avoid cross-test contamination global_var = 0; } void test_func_under_test_case_X(void) { global_var = X; TEST_ASSERT_EQUAL(SOME_VALUE, func_under_test()); } void test_func_under_test_case_Y(void) { global_var = Y; TEST_ASSERT_EQUAL(SOME_OTHER_VALUE, func_under_test()); } void test_func_under_test_default_case(void) { global_var = 999; // A value not covered by any case TEST_ASSERT_EQUAL(DEFAULT_VALUE, func_under_test()); }
Approach 2: Add a Test-Only Setter Function (No Global Exposure)
If you prefer not to make the variable global even in test builds, add a lightweight setter function that's only compiled during testing. This keeps your production code's encapsulation intact.
Step 1: Add the Setter to file_under_test.c
static int global_var; #ifdef TESTING // Test-only function to set the static variable void set_global_var(int value) { global_var = value; } #endif int func_under_test(){ // ... same as before }
Step 2: Declare the Setter in Your Test File
Add a prototype for the setter in your test file (or create a test-only header if you have multiple tests needing this):
#include "unity.h" #include "file_under_test.h" // Declare the test-only setter void set_global_var(int value); void setUp(void) { // Reset the variable before each test set_global_var(0); } void test_func_under_test_case_X(void) { set_global_var(X); TEST_ASSERT_EQUAL(SOME_VALUE, func_under_test()); } // ... repeat for other cases and default
Verifying Coverage
Once your tests are written, run ceedling test:all to execute them. Ceedling will generate a coverage report (look in the build/reports/coverage directory) that shows you which parts of func_under_test are covered. As long as you've written a test for every case in the switch statement (including the default), you'll hit 100% branch coverage for that function.
内容的提问来源于stack exchange,提问作者Nitesh

