@azure/umock-c
umock-c AI Coding Instructions
Install
agr install @azure/umock-c --target copilotWrites 1 file into .github/copilot-instructions.md, pinned to git-a2df81e0.
- .github/copilot-instructions.md
Document
umock-c AI Coding Instructions
Project Overview
umock-c is a C mocking library designed for Azure C projects, providing macro-based mock generation, expectation recording, and call verification. It enables sophisticated unit testing through compile-time mock generation and runtime call tracking with rich assertion capabilities.
Core Architecture
Mock Generation System
MOCKABLE_FUNCTION(modifiers, result, function, ...): Core macro that generates mockable function declarations and implementationsENABLE_MOCKS: Compilation flag that switches between production code (declarations only) and test code (full mock implementations)inc/umock_c/umock_c_prod.h: Production header that enablesMOCKABLE_FUNCTIONfor both production and test builds- Macro Metaprogramming: Heavily uses
macro-utils-cfor complex macro expansion and code generation
Mock Control Flow
// Production header (dependency.h)
#include "umock_c/umock_c_prod.h"
MOCKABLE_FUNCTION(, int, dependency_function, int, arg);
// Test file pattern
#define ENABLE_MOCKS
#include "dependency.h" // Generates mock implementations
#undef ENABLE_MOCKS
#include "unit_under_test.h" // Real implementation
Call Recording & Verification
STRICT_EXPECTED_CALL(call): Records expected calls with argument validationEXPECTED_CALL(call): Records expected calls without argument validation by default- Call Modifiers: Chainable modifiers like
.SetReturn(),.IgnoreArgument(),.ValidateArgument() umock_c_get_expected_calls()/umock_c_get_actual_calls(): String representations for test assertions
Key Development Patterns
Test Structure (AAA Pattern)
TEST_FUNCTION(function_under_test_with_dependency_succeeds)
{
// arrange
STRICT_EXPECTED_CALL(dependency_function(42))
.SetReturn(0);
// act
int result = function_under_test();
// assert
ASSERT_ARE_EQUAL(int, 0, result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}
Mock Filtering (Performance Optimization)
#define ENABLE_MOCK_FILTERING
#define please_mock_specific_function MOCK_ENABLED
#define ENABLE_MOCKS
#include "large_header.h" // Only generates mocks for selected functions
#undef ENABLE_MOCKS
Global Mock Configuration
// Test suite setup
REGISTER_GLOBAL_MOCK_HOOK(malloc, my_malloc_hook);
REGISTER_GLOBAL_MOCK_RETURN(function_name, success_value);
REGISTER_GLOBAL_MOCK_FAIL_RETURN(function_name, failure_value);
Call Modifiers (Fluent Interface)
STRICT_EXPECTED_CALL(function(arg1, arg2, buffer))
.SetReturn(42)
.IgnoreArgument_arg1()
.ValidateArgumentBuffer_buffer(expected_data, data_size)
.CopyOutArgumentBuffer_buffer(output_data, output_size);
Call Modifiers Reference
Call modifiers provide a fluent interface for configuring mock expectations. Each modifier returns a structure that allows chaining further modifiers. The last modifier in a chain overrides previous modifiers if any collision occurs.
Argument Control Modifiers
IgnoreArgument Family
// Ignore specific argument by name (preferred)
STRICT_EXPECTED_CALL(function(42, "test", buffer))
.IgnoreArgument_arg1() // Ignores first argument regardless of value
.IgnoreArgument_buffer(); // Ignores buffer argument
// Ignore argument by index (0-based)
STRICT_EXPECTED_CALL(function(42, "test", buffer))
.IgnoreArgument(0) // Ignores first argument (42)
.IgnoreArgument(2); // Ignores third argument (buffer)
// Ignore all arguments at once
STRICT_EXPECTED_CALL(function(42, "test", buffer))
.IgnoreAllArguments(); // All arguments ignored, only function name matters
// Automatic ignore with IGNORED_ARG
STRICT_EXPECTED_CALL(function(IGNORED_ARG, "test", IGNORED_ARG)); // Equivalent to IgnoreArgument on positions 0 and 2
ValidateArgument Family
// Validate specific argument by name (overrides ignore)
EXPECTED_CALL(function(42, "test", buffer)) // EXPECTED_CALL ignores all by default
.ValidateArgument_arg1() // But validate first argument
.ValidateArgument_arg2(); // And second argument
// Validate argument by index
EXPECTED_CALL(function(42, "test", buffer))
.ValidateArgument(0) // Validate first argument
.ValidateArgument(1); // Validate second argument
Return Value Modifiers
SetReturn and SetFailReturn
// Set successful return value
STRICT_EXPECTED_CALL(malloc(100))
.SetReturn((void*)0x1234); // Return specific pointer
// Set both success and failure return values
STRICT_EXPECTED_CALL(network_send(data, size))
.SetReturn(0) // Success case
.SetFailReturn(-1); // Failure case for negative testing
// Capture the return value that was actually returned
int captured_return;
STRICT_EXPECTED_CALL(get_status())
.CaptureReturn(&captured_return); // Store return value in variable
// After call: captured_return contains the value that was returned
Buffer and Pointer Argument Modifiers
ValidateArgumentBuffer Family
// Validate buffer contents by name
char expected_data[] = { 0x01, 0x02, 0x03, 0x04 };
STRICT_EXPECTED_CALL(write_data(buffer, 4))
.ValidateArgumentBuffer_buffer(expected_data, sizeof(expected_data));
// Validate buffer contents by index
STRICT_EXPECTED_CALL(write_data(buffer, 4))
.ValidateArgumentBuffer(0, expected_data, sizeof(expected_data));
// Buffer validation automatically ignores the pointer value itself
// (only contents matter, not the actual pointer address)
CopyOutArgumentBuffer Family
// Copy data to output buffer by argument name
char output_data[] = { 0xAA, 0xBB, 0xCC, 0xDD };
STRICT_EXPECTED_CALL(read_data(output_buffer, buffer_size))
.CopyOutArgumentBuffer_output_buffer(output_data, sizeof(output_data));
// Copy data to output buffer by index
STRICT_EXPECTED_CALL(read_data(output_buffer, buffer_size))
.CopyOutArgumentBuffer(0, output_data, sizeof(output_data));
// The buffer argument is automatically ignored (marked as out parameter)
// Memory is copied, so original data can be modified after the call
CopyOutArgument (Single Values)
// Copy single value to pointer argument
int output_value = 42;
STRICT_EXPECTED_CALL(get_value(&result))
.CopyOutArgument(output_value); // Copies value to *result
// Works with any type that has registered copy handlers
MY_STRUCT output_struct = { .field1 = 10, .field2 = "test" };
STRICT_EXPECTED_CALL(get_struct(&result_struct))
.CopyOutArgument(output_struct);
Advanced Argument Validation
ValidateArgumentValue Family
// Validate against variable value (not literal)
int expected_id = 42;
STRICT_EXPECTED_CALL(process_item(expected_id, data))
.ValidateArgumentValue_id(&expected_id); // Validates against variable content
// This allows changing expected value before the call
expected_id = 99; // Now the call expects 99 instead of 42
// Validate argument as different type
void* generic_ptr = &my_struct;
STRICT_EXPECTED_CALL(process_generic(generic_ptr))
.ValidateArgumentValue_generic_ptr_AsType(UMOCK_TYPE(MY_STRUCT));
CaptureArgumentValue Family
// Capture argument value at call time
int captured_arg;
STRICT_EXPECTED_CALL(process_value(42))
.CaptureArgumentValue_value(&captured_arg);
// After the call, captured_arg contains the actual value passed
// This is useful for verifying computed arguments
Special Behavior Modifiers
IgnoreAllCalls
// Allow multiple matching calls without reporting errors
STRICT_EXPECTED_CALL(log_message(IGNORED_ARG))
.IgnoreAllCalls(); // Any number of log_message calls OK
// Useful for logging functions, cleanup functions, etc.
// No "unexpected call" errors even if called multiple times
// No "missing call" errors if never called
CallCannotFail (Negative Testing)
// Mark call as unable to fail during negative testing
STRICT_EXPECTED_CALL(critical_system_call())
.CallCannotFail();
// When using umock_c_negative_tests_fail_call(), this call is skipped
// Useful for calls that represent system state that cannot fail
Call Modifier Patterns and Best Practices
Chaining Order and Precedence
// Modifiers can be chained in any order
STRICT_EXPECTED_CALL(complex_function(arg1, buffer, size, output))
.SetReturn(0) // Set return value
.IgnoreArgument_arg1() // Ignore first argument
.ValidateArgumentBuffer_buffer(data, len) // Validate buffer contents
.CopyOutArgumentBuffer_output(result, len) // Copy data to output
.ValidateArgument_size(); // But validate size argument
// Later modifiers override earlier ones for the same argument
STRICT_EXPECTED_CALL(function(arg))
.IgnoreArgument_arg() // Initially ignore
.ValidateArgument_arg(); // Override: now validate
// This allows progressive refinement of expectations
Common Patterns
Out Parameter Handling
// Function that fills output buffer and returns status
int error_code = 0;
char response_data[] = "SUCCESS";
STRICT_EXPECTED_CALL(api_call(request, response_buffer, &status))
.SetReturn(100) // Bytes written
.CopyOutArgumentBuffer_response_buffer(response_data, strlen(response_data))
.CopyOutArgument(error_code); // Set *status = 0
Variable Argument Validation
// When expected values are computed at runtime
TEST_FUNCTION(test_with_dynamic_values)
{
// arrange
int current_time = get_current_timestamp();
char* expected_message = generate_log_message();
STRICT_EXPECTED_CALL(log_with_timestamp(current_time, expected_message))
.ValidateArgumentValue_timestamp(¤t_time)
.ValidateArgumentValue_message(&expected_message);
// act
function_under_test();
// assert
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
free(expected_message);
}
Buffer Content Verification
// Verify exact buffer contents while ignoring buffer address
void test_buffer_writing()
{
// arrange
char expected_header[] = { 0x42, 0x43, 0x44, 0x45 };
char output_buffer[100];
STRICT_EXPECTED_CALL(write_header(output_buffer))
.ValidateArgumentBuffer_buffer(expected_header, sizeof(expected_header));
// act
result = write_data_packet(output_buffer);
// assert
ASSERT_ARE_EQUAL(int, 0, result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}
Automatic Mock Generation Function
The add_mock_library() CMake function automatically generates mock libraries:
# Generates library_name_mocks target with precompiled headers
add_mock_library(my_library
HEADER_FILES
"header1.h"
"header2.h"
)
Type System Integration
Built-in Type Support
- Native C Types: All standard C types supported via
umocktypes_c.h - Windows Types:
HRESULT,DWORD,HANDLE, etc. viaumocktypes_windows.h - stdint Types:
int32_t,uint64_t, etc. viaumocktypes_stdint.h - String Types:
char*,const char*,wchar_t*via dedicated headers
Custom Type Registration
// Define custom type handlers
IMPLEMENT_UMOCK_C_ENUM_TYPE(MY_ENUM, VALUE1, VALUE2, VALUE3);
// Register in test setup
REGISTER_UMOCK_VALUE_TYPE(MY_STRUCT); // Auto-derives function names
REGISTER_UMOCK_ALIAS_TYPE(MY_HANDLE, void*); // Alias existing type
Type Handler Implementation
Every custom type requires four handler functions that umock-c uses for argument comparison and display:
// 1. Stringify function - converts value to string representation
char* umock_stringify_MY_STRUCT(const MY_STRUCT* value)
{
char* result;
if (value == NULL)
{
result = NULL;
}
else
{
char temp_buffer[256];
int length = sprintf(temp_buffer, "{ field1=%d, field2=%s }", value->field1, value->field2);
if (length < 0)
{
result = NULL;
}
else
{
result = (char*)malloc(length + 1);
if (result != NULL)
{
memcpy(result, temp_buffer, length + 1);
}
}
}
return result;
}
// 2. Are equal function - compares two values for equality
int umock_are_equal_MY_STRUCT(const MY_STRUCT* left, const MY_STRUCT* right)
{
int result;
if (left == right)
{
result = 1; // Same pointer or both NULL
}
else if ((left == NULL) || (right == NULL))
{
result = 0; // One is NULL, other is not
}
else
{
result = (left->field1 == right->field1 &&
strcmp(left->field2, right->field2) == 0) ? 1 : 0;
}
return result;
}
// 3. Copy function - creates a copy of the value
int umock_copy_MY_STRUCT(MY_STRUCT* destination, const MY_STRUCT* source)
{
int result;
if ((destination == NULL) || (source == NULL))
{
result = MU_FAILURE;
}
else
{
destination->field1 = source->field1;
if (source->field2 != NULL)
{
destination->field2 = (char*)malloc(strlen(source->field2) + 1);
if (destination->field2 == NULL)
{
result = MU_FAILURE;
}
else
{
strcpy(destination->field2, source->field2);
result = 0;
}
}
else
{
destination->field2 = NULL;
result = 0;
}
}
return result;
}
// 4. Free function - releases resources allocated by copy
void umock_free_MY_STRUCT(MY_STRUCT* value)
{
if (value != NULL)
{
if (value->field2 != NULL)
{
free(value->field2);
value->field2 = NULL;
}
}
}
Type Registration Patterns
// Manual registration with explicit function names
REGISTER_UMOCK_VALUE_TYPE(MY_STRUCT,
umock_stringify_MY_STRUCT,
umock_are_equal_MY_STRUCT,
umock_copy_MY_STRUCT,
umock_free_MY_STRUCT);
// Automatic registration (derives function names from type)
REGISTER_UMOCK_VALUE_TYPE(MY_STRUCT);
// Alias registration (reuses handlers from existing type)
REGISTER_UMOCK_ALIAS_TYPE(MY_HANDLE, void*);
// Common registration in test suite initialization
TEST_SUITE_INITIALIZE(TestClassInitialize)
{
ASSERT_ARE_EQUAL(int, 0, umock_c_init(test_on_umock_c_error));
REGISTER_UMOCK_VALUE_TYPE(MY_STRUCT);
ASSERT_ARE_EQUAL(int, 0, umocktypes_charptr_register_types());
ASSERT_ARE_EQUAL(int, 0, umocktypes_stdint_register_types());
}
Advanced Type Patterns
// Pointer type declaration
DECLARE_UMOCK_POINTER_TYPE_FOR_TYPE(MY_STRUCT, my_struct);
// Runtime type validation
STRICT_EXPECTED_CALL(function_with_void_ptr(&data))
.ValidateArgumentValue_ptr_AsType(UMOCK_TYPE(MY_STRUCT));
Advanced Features
Negative Testing Framework
// Automated failure injection testing
STRICT_EXPECTED_CALL(function1()).SetReturn(0).SetFailReturn(1);
STRICT_EXPECTED_CALL(function2()).SetReturn(0).SetFailReturn(1);
umock_c_negative_tests_snapshot();
for (i = 0; i < umock_c_negative_tests_call_count(); i++)
{
umock_c_negative_tests_reset();
umock_c_negative_tests_fail_call(i);
int result = function_under_test();
ASSERT_ARE_NOT_EQUAL(int, 0, result);
}
Paired Calls Tracking (Resource Leak Detection)
// Registers create/destroy pairs for leak detection
REGISTER_UMOCKC_PAIRED_CREATE_DESTROY_CALLS(handle_create, handle_destroy);
Multithreading Support
// Initialize with custom lock factory for thread safety
umock_c_init_with_lock_factory(error_callback, lock_factory_func, lock_params);
Build System Integration
CMake Configuration
# Standard dependency inclusion
if ((NOT TARGET umock_c) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/umock-c/CMakeLists.txt))
add_subdirectory(deps/umock-c)
endif()
# Link to test targets
target_link_libraries(my_test_exe umock_c)
# Test execution options
-Drun_unittests=ON
-Drun_int_tests=ON
-Duse_cppunittest=ON # Windows CppUnitTest integration
Test Categories & Organization
- Unit Tests (
*_ut/): Component-level testing with mocked dependencies - Integration Tests (
*_int/): Cross-component testing with real implementations - Test Structure: Each test directory has its own
CMakeLists.txtusingbuild_test_folder()function
Mock Precompiled Headers (Performance Optimization)
The build_test_artifacts() function integrates with the c-testrunnerswitcher build system to provide precompiled header optimization for mock-heavy test projects:
# CMakeLists.txt for test project
build_test_artifacts(my_component_ut ${CMAKE_CURRENT_LIST_DIR}
MOCK_PRECOMPILE_HEADERS
"dependency1.h"
"dependency2.h"
"large_dependency.h"
)
MOCK_PRECOMPILE_HEADERS
- Purpose: Precompiles headers that should be included between
#define ENABLE_MOCKSand#undef ENABLE_MOCKS - Usage: List headers that generate many mocks and are included in multiple test files
- Generated Target: Creates
${target_name}_mocks.hand${target_name}_mocks.cwith precompiled headers - Integration: Automatically generates mock functions for listed headers
CRITICAL REQUIREMENT: All headers that exist in the unit test file between
#define ENABLE_MOCKSand#undef ENABLE_MOCKSMUST be listed inMOCK_PRECOMPILE_HEADERS. Failing to include them will cause build errors due to missing mock implementations.
// Test file pattern - ALL these headers must be in MOCK_PRECOMPILE_HEADERS
#define ENABLE_MOCKS
#include "dependency1.h" // ✅ Must be in MOCK_PRECOMPILE_HEADERS
#include "dependency2.h" // ✅ Must be in MOCK_PRECOMPILE_HEADERS
#include "large_dependency.h" // ✅ Must be in MOCK_PRECOMPILE_HEADERS
#undef ENABLE_MOCKS
// Corresponding CMakeLists.txt - must match exactly
build_test_artifacts(my_component_ut ${CMAKE_CURRENT_LIST_DIR}
MOCK_PRECOMPILE_HEADERS
"dependency1.h" # Must match test file includes
"dependency2.h" # Must match test file includes
"large_dependency.h" # Must match test file includes
)
# This generates a precompiled header file with structure:
# #define ENABLE_MOCKS
# #define ENABLE_MOCKS_DECL
# #include "dependency1.h"
# #include "dependency2.h"
# #include "large_dependency.h"
# #undef ENABLE_MOCKS_DECL
# #undef ENABLE_MOCKS
build_test_artifacts(my_component_ut ${CMAKE_CURRENT_LIST_DIR}
MOCK_PRECOMPILE_HEADERS
"azure_c_shared_utility/gballoc.h"
"azure_c_shared_utility/crt_abstractions.h"
"azure_uamqp_c/amqp_management.h"
)
NO_MOCK_PRECOMPILE_HEADERS
- Purpose: Precompiles headers that should be included before and outside of
#define ENABLE_MOCKSand#undef ENABLE_MOCKS - Usage: Headers that should not have mocks generated but are frequently included
- Effect: Includes headers in the precompiled header without mock generation
BEST PRACTICE: Any headers that are included in the unit test file before
#define ENABLE_MOCKS(except standard C runtime headers likeinterlocked.h) should be listed inNO_MOCK_PRECOMPILE_HEADERSfor optimal build performance.
// Test file pattern - headers before ENABLE_MOCKS should be in NO_MOCK_PRECOMPILE_HEADERS
#include "testrunnerswitcher.h" // ✅ Should be in NO_MOCK_PRECOMPILE_HEADERS
#include "standard_includes.h" // ✅ Should be in NO_MOCK_PRECOMPILE_HEADERS
#include "constants.h" // ✅ Should be in NO_MOCK_PRECOMPILE_HEADERS
#include "c_pal/interlocked.h" // ✅ Should be in NO_MOCK_PRECOMPILE_HEADERS
#define ENABLE_MOCKS
#include "dependency1.h" // ✅ Should be in MOCK_PRECOMPILE_HEADERS
#include "dependency2.h" // ✅ Should be in MOCK_PRECOMPILE_HEADERS
#undef ENABLE_MOCKS
// Corresponding CMakeLists.txt
build_test_artifacts(component_ut ${CMAKE_CURRENT_LIST_DIR}
NO_MOCK_PRECOMPILE_HEADERS
"testrunnerswitcher.h" # Match test file includes
"standard_includes.h" # Match test file includes
"constants.h" # Match test file includes
"c_pal/interlocked.h" # c_pal repository header
MOCK_PRECOMPILE_HEADERS
"dependency1.h" # Headers between ENABLE_MOCKS
"dependency2.h" # Headers between ENABLE_MOCKS
)
# This generates headers that are included BEFORE the ENABLE_MOCKS block
build_test_artifacts(component_ut ${CMAKE_CURRENT_LIST_DIR}
NO_MOCK_PRECOMPILE_HEADERS
"standard_includes.h" # Headers without mockable functions
"constants.h" # Configuration headers
MOCK_PRECOMPILE_HEADERS
"mockable_dependency.h" # Headers with MOCKABLE_FUNCTION
)
Build Performance Considerations
# ✅ Good candidates for MOCK_PRECOMPILE_HEADERS
MOCK_PRECOMPILE_HEADERS
"azure_c_shared_utility/gballoc.h" # Complex memory allocation mocks
"azure_uamqp_c/session.h" # Large interface with many functions
"azure_iot_common/iothub_client.h" # Frequently used across tests
# ✅ Good candidates for NO_MOCK_PRECOMPILE_HEADERS
NO_MOCK_PRECOMPILE_HEADERS
"macro_utils/macro_utils.h" # Macro utilities without mockable functions
"constants.h" # Configuration headers
"common_includes.h" # Standard library wrappers
# ✅ Good candidates for MOCK_PRECOMPILE_HEADERS
MOCK_PRECOMPILE_HEADERS
"azure_c_shared_utility/gballoc.h" # Complex memory allocation mocks
"azure_uamqp_c/session.h" # Large interface with many functions
"azure_iot_common/iothub_client.h" # Frequently used across tests
# ❌ Poor candidates - avoid precompilation overhead
# "simple_interface.h" # Only 1-2 functions
# "test_specific_header.h" # Used by single test file
# "infrequently_used.h" # Rarely included
Build Performance Considerations
# ✅ Good candidates for MOCK_PRECOMPILE_HEADERS
MOCK_PRECOMPILE_HEADERS
"azure_c_shared_utility/gballoc.h" # Complex memory allocation mocks
"azure_uamqp_c/session.h" # Large interface with many functions
"azure_iot_common/iothub_client.h" # Frequently used across tests
# ✅ Good candidates for NO_MOCK_PRECOMPILE_HEADERS
NO_MOCK_PRECOMPILE_HEADERS
"testrunnerswitcher.h" # Test framework headers
"macro_utils/macro_utils.h" # Macro utilities without mockable functions
"constants.h" # Configuration headers
"common_includes.h" # Standard library wrappers
# ❌ Don't precompile these
# Standard CRT headers: <interlocked.h>, <stdio.h>, <stdlib.h>
# "simple_interface.h" # Only 1-2 functions
# "test_specific_header.h" # Used by single test file
# "infrequently_used.h" # Rarely included
Critical Build Requirements
# This generates a precompiled header file with structure:
# #define ENABLE_MOCKS
# #define ENABLE_MOCKS_DECL
# #include "dependency1.h"
# #include "dependency2.h"
# #include "large_dependency.h"
# #undef ENABLE_MOCKS_DECL
# #undef ENABLE_MOCKS
build_test_artifacts(my_component_ut ${CMAKE_CURRENT_LIST_DIR}
MOCK_PRECOMPILE_HEADERS
"azure_c_shared_utility/gballoc.h"
"azure_c_shared_utility/crt_abstractions.h"
"azure_uamqp_c/amqp_management.h"
)
Generated Mock Library Structure
When MOCK_PRECOMPILE_HEADERS or NO_MOCK_PRECOMPILE_HEADERS is used, the build system generates:
# Generated automatically by build_test_artifacts()
# my_component_ut_mocks.h contains:
#ifndef MY_COMPONENT_UT_MOCKS_H
#define MY_COMPONENT_UT_MOCKS_H
// NO_MOCK_PRECOMPILE_HEADERS content (outside ENABLE_MOCKS)
#include "standard_includes.h"
#include "constants.h"
// MOCK_PRECOMPILE_HEADERS content (inside ENABLE_MOCKS)
#define ENABLE_MOCKS
#define ENABLE_MOCKS_DECL
#include "dependency1.h"
#include "dependency2.h"
#undef ENABLE_MOCKS_DECL
#undef ENABLE_MOCKS
#endif // MY_COMPONENT_UT_MOCKS_H
# Result:
# - my_component_ut_mocks.h (generated header with proper include order)
# - my_component_ut_mocks.c (generated source with mock implementations)
# - Precompiled header: my_component_ut_mocks.h.pch
Project-Specific Conventions
Header Inclusion Strategy
// Production code pattern
#include "umock_c/umock_c_prod.h" // Enables MOCKABLE_FUNCTION
// Test file pattern
#include "testrunnerswitcher.h" // Test framework
#define ENABLE_MOCKS
#include "dependency1.h" // Generate mocks for dependencies
#include "dependency2.h"
#undef ENABLE_MOCKS
#include "unit_under_test.h" // Real implementation
Error Handling Patterns
- Error Callbacks:
ON_UMOCK_C_ERRORfunction pointer for custom error handling - Error Codes:
UMOCK_C_ERROR_CODEenum for specific error conditions - Automatic Error Reporting: Most mock validation failures automatically call error callbacks
Static Mock Generation
// Avoid symbol collisions in multi-unit test libraries
#define UMOCK_STATIC static
#define ENABLE_MOCKS
#include "dependency.h"
#include "unit_under_test.c" // Include source directly for static visibility
Testing Best Practices
Mock Expectation Patterns
- Use
STRICT_EXPECTED_CALLfor precise argument validation - Use
EXPECTED_CALLwhen arguments don't matter, apply modifiers selectively - Chain modifiers for complex expectations:
.SetReturn().IgnoreArgument().ValidateArgumentBuffer() - Always verify calls match:
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls())
Test Initialization
TEST_SUITE_INITIALIZE(TestClassInitialize)
{
ASSERT_ARE_EQUAL(int, 0, umock_c_init(test_on_umock_c_error));
ASSERT_ARE_EQUAL(int, 0, umocktypes_charptr_register_types());
}
TEST_FUNCTION_CLEANUP(TestMethodCleanup)
{
umock_c_reset_all_calls();
}
Custom Type Testing
// Enum type support in tests
TEST_DEFINE_ENUM_TYPE(MY_ENUM, MY_ENUM_VALUES);
// Test assertions for custom types
ASSERT_ARE_EQUAL(MY_ENUM, EXPECTED_VALUE, actual_value);
External Dependencies & Standards
Dependency References
Refer to dependency-specific instructions for comprehensive patterns:
- c-build-tools: Build infrastructure, coding standards, quality gates (#file:../deps/c-build-tools/.github/copilot-instructions.md)
- AI Context & Coding Standards: #file:../deps/azure-messaging-ai-context/.github/copilot-instructions.md (git conventions, terminal rules, agent skills)
- macro-utils-c: Macro metaprogramming patterns (#file:../deps/macro-utils-c/.github/copilot-instructions.md)
- c-logging: Logging integration for debugging (#file:../deps/c-logging/.github/copilot-instructions.md)
- ctest: Test framework integration (#file:../deps/ctest/.github/copilot-instructions.md)
- c-testrunnerswitcher: Cross-platform test execution (#file:../deps/c-testrunnerswitcher/.github/copilot-instructions.md)
Coding Standards Compliance
CRITICAL: All code must follow comprehensive standards in #file:../deps/azure-messaging-ai-context/.github/general_coding_instructions.md:
- Function naming (snake_case with module prefixes like
umock_c_*,umocktypes_*) - Parameter validation patterns and error handling
- Header inclusion order and memory management
- Requirements traceability (
SRS_UMOCK_C_*,Codes_SRS_UMOCK_C_*,Tests_SRS_UMOCK_C_*) - Memory allocation patterns and cleanup protocols
Key Files & Components
inc/umock_c/umock_c.h: Main API with core macros and functionsinc/umock_c/umock_c_prod.h: Production-safe header enablingMOCKABLE_FUNCTIONsrc/umock_c.c: Core implementation with call recording and verificationsrc/umock_c_negative_tests.c: Negative testing framework implementationCMakeLists.txt: Definesadd_mock_library()function for automated mock generationdevdoc/*.md: Comprehensive requirements documentation for all features
This mocking framework's strength lies in its macro-based approach that generates type-safe mocks at compile time while providing runtime flexibility for test expectations and call verification.
Common Patterns and Anti-Patterns
Recommended Patterns
// ✅ Use stack contexts for short-lived logging
LOG_CONTEXT_LOCAL_DEFINE(local_ctx, NULL, LOG_CONTEXT_PROPERTY(int32_t, id, 123));
LOGGER_LOG(LOG_LEVEL_INFO, &local_ctx, "Operation completed");
// ✅ Check context creation results for dynamic contexts
LOG_CONTEXT_HANDLE ctx;
if (LOG_CONTEXT_CREATE(ctx, NULL, /* properties */) == 0)
{
LOGGER_LOG(LOG_LEVEL_INFO, ctx, "Message");
LOG_CONTEXT_DESTROY(ctx);
}
// ✅ Use appropriate log levels
LOGGER_LOG(LOG_LEVEL_CRITICAL, NULL, "System is shutting down"); // Fatal errors
LOGGER_LOG(LOG_LEVEL_ERROR, NULL, "Operation failed"); // Recoverable errors
LOGGER_LOG(LOG_LEVEL_WARNING, NULL, "Retrying operation"); // Potential issues
LOGGER_LOG(LOG_LEVEL_INFO, NULL, "User logged in"); // Normal events
LOGGER_LOG(LOG_LEVEL_VERBOSE, NULL, "Detailed debug info"); // Debug information
Critical Mistakes to Avoid
❌ CopyOutArgumentBuffer with Stack Variables
// DANGEROUS - stack variable goes out of scope
TEST_FUNCTION(bad_copy_out_example)
{
// arrange
{
char stack_buffer[] = "temporary data"; // Stack variable
STRICT_EXPECTED_CALL(get_data(output_buffer))
.CopyOutArgumentBuffer_output_buffer(stack_buffer, sizeof(stack_buffer));
} // stack_buffer goes out of scope here!
// act - UNDEFINED BEHAVIOR: stack_buffer memory is invalid
get_data(my_buffer);
}
// ✅ CORRECT - use static or heap allocated data
TEST_FUNCTION(good_copy_out_example)
{
// arrange
static char persistent_buffer[] = "persistent data"; // Static storage
STRICT_EXPECTED_CALL(get_data(output_buffer))
.CopyOutArgumentBuffer_output_buffer(persistent_buffer, sizeof(persistent_buffer));
// act - SAFE: persistent_buffer remains valid
get_data(my_buffer);
}
❌ Forgetting to Register Custom Types
// DANGEROUS - using custom types without registration
typedef struct MY_STRUCT_TAG { int value; } MY_STRUCT;
TEST_FUNCTION(unregistered_type_failure)
{
MY_STRUCT test_data = { 42 };
// This will FAIL at runtime - MY_STRUCT not registered
STRICT_EXPECTED_CALL(process_struct(test_data)); // Runtime error!
}
// ✅ CORRECT - register types in test suite initialization
TEST_SUITE_INITIALIZE(TestSuiteInit)
{
ASSERT_ARE_EQUAL(int, 0, umock_c_init(test_on_umock_c_error));
// Register custom types BEFORE using them
REGISTER_UMOCK_VALUE_TYPE(MY_STRUCT);
ASSERT_ARE_EQUAL(int, 0, umocktypes_charptr_register_types());
}
TEST_FUNCTION(registered_type_success)
{
MY_STRUCT test_data = { 42 };
// Now this works correctly
STRICT_EXPECTED_CALL(process_struct(test_data));
}
❌ Missing umock_c_prod.h After Disabling Mocks
// DANGEROUS - missing production header after undef
#define ENABLE_MOCKS
#include "dependency1.h" // Generates mocks
#include "dependency2.h" // Generates mocks
#undef ENABLE_MOCKS
// Missing: #include "umock_c/umock_c_prod.h"
#include "dependency3.h" // BROKEN - no MOCKABLE_FUNCTION support
#include "unit_under_test.h" // May have issues with real function calls
// ✅ CORRECT - include production header after undef
#define ENABLE_MOCKS
#include "dependency1.h" // Generates mocks
#include "dependency2.h" // Generates mocks
#undef ENABLE_MOCKS
#include "umock_c/umock_c_prod.h" // REQUIRED to switch back to not generating mocks
#include "dependency3.h" // Now works correctly
#include "unit_under_test.h" // Real function support available
❌ Incorrect Call Expectation Verification
// DANGEROUS - forgetting to verify call matching
TEST_FUNCTION(incomplete_verification)
{
// arrange
STRICT_EXPECTED_CALL(dependency_function(42));
// act
function_under_test();
// Missing verification - test passes even if calls don't match!
// ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}
// ✅ CORRECT - always verify call matching
TEST_FUNCTION(complete_verification)
{
// arrange
STRICT_EXPECTED_CALL(dependency_function(42));
// act
function_under_test();
// assert - REQUIRED verification
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
}
❌ Memory Leaks in Test Cleanup
// DANGEROUS - not cleaning up allocated test data
TEST_FUNCTION(memory_leak_example)
{
// arrange
char* dynamic_data = malloc(100);
strcpy(dynamic_data, "test data");
STRICT_EXPECTED_CALL(process_data(dynamic_data));
// act
process_data(dynamic_data);
// Missing cleanup - MEMORY LEAK!
// free(dynamic_data);
}
// ✅ CORRECT - always clean up in test cleanup or function end
TEST_FUNCTION(proper_cleanup_example)
{
// arrange
char* dynamic_data = malloc(100);
strcpy(dynamic_data, "test data");
STRICT_EXPECTED_CALL(process_data(dynamic_data));
// act
process_data(dynamic_data);
// assert
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// cleanup
free(dynamic_data);
}
Trust
Not scanned yet. Artifacts are graded after they are crawled, so a recently discovered one may have no result for a while.
Versions
git-a2df81e040232026-08-04