This document describes the native testing setup that allows you to run unit tests for ESP32 code on your local development machine without requiring actual hardware.
The [env:native] environment is configured to:
- Use the
nativeplatform (runs on your local machine) - Override the Arduino framework to avoid ESP32 dependencies
- Build only the specific source files needed for testing
- Include mock headers from
test/mocks/
[env:native]
platform = native
framework = # Override to prevent Arduino framework inheritance
lib_deps = # Override to prevent library dependencies
test_build_src = yes # Enable building source files for tests
build_src_filter =
-<*> # Exclude all files by default
+<util/timing_manager.cpp> # Include only the file being tested
test_filter = test_timing_manager
build_flags =
-std=c++11
-Iinclude # Include production headers
-Itest/mocks # Include mock headers (takes precedence)
-DNATIVE_TEST # Define flag for conditional compilation
lib_compat_mode = offAll mock files are located in test/mocks/:
test/mocks/
├── Arduino.h # Mock Arduino core
├── Preferences.h # Mock ESP32 Preferences library
├── esp32_mocks.h # Common ESP32 mocks (logging, String class)
├── esp_log.h # Mock ESP logging
├── esp_sleep.h # Mock ESP sleep functions
├── time_manager.h # Mock TimeManager class
└── config_manager.cpp # Mock ConfigManager implementation
A mock String class that inherits from std::string and provides Arduino-compatible methods:
indexOf(char c)substring(int start, int end)toInt(),toFloat()length(),c_str()
Mock logging macros that output to stdout:
ESP_LOGI,ESP_LOGW,ESP_LOGE,ESP_LOGD,ESP_LOGVRTC_DATA_ATTR(no-op for native)
A complete mock of ESP32's Preferences library using std::map for storage:
begin(),end(),clear()- Getters:
getInt(),getFloat(),getString(), etc. - Setters:
putInt(),putFloat(),putString(), etc.
A simplified mock implementation that:
- Defines
rtcConfigwith default values - Implements all public methods
- Uses
#ifdef NATIVE_TESTto compile only for native tests
The production code uses conditional compilation to switch between real and mock headers:
#ifdef NATIVE_TEST
#include "time_manager.h" // Mock from test/mocks/
#include "esp_log.h" // Mock from test/mocks/
#else
#include "util/time_manager.h" // Real header from include/
#include <esp_log.h> // Real ESP-IDF header
#endifTests are organized in test/test_timing_manager/:
test/test_timing_manager/
├── test_sleep_duration.cpp # Test cases
└── mocks/
└── config_manager.cpp # Mock copied here for test build
pio test -e nativepio test -e native -vpio test -e native -vvv[INFO][TIMING_MGR] Calculating sleep duration - Display mode: 1, Current time: 1761773673
[INFO][TIMING_MGR] Last updates - Weather: 0 seconds, Departure: 0 seconds
[INFO][TIMING_MGR] Weather interval: 1 hours (3600 seconds), Next weather update: 1761773673
[INFO][TIMING_MGR] Only weather update needed at: 1761773673 seconds
[INFO][TIMING_MGR] Final sleep duration: 30 seconds (0 minutes)
test/test_timing_manager/test_sleep_duration.cpp:111:test_getNextSleepDurationSeconds_weather_only_mode:PASS
34 Tests 0 Failures 0 Ignored
OK
- Fast Testing: No need to flash firmware to hardware
- Easy Debugging: Use standard C++ debugging tools
- CI/CD Ready: Can run in automated build pipelines
- Isolation: Test business logic without hardware dependencies
- No Code Changes: Production code remains clean with minimal conditional compilation
To add tests for a new module:
- Create a new test directory:
test/test_<module_name>/ - Create test file:
test_<feature>.cpp - Add required mocks to
test/mocks/if needed - Update
platformio.inibuild_src_filter to include the source file - Run tests with
pio test -e native -v
- Check that source files are included in
build_src_filter - Verify mock implementations exist for all dependencies
- Ensure mock headers are in
test/mocks/ - Check that
-Itest/mocksis in build_flags - Verify conditional includes in source files
- Make sure
framework =is explicitly set in[env:native] - Set
lib_deps =to override inherited dependencies
- Add more comprehensive test cases
- Mock additional ESP32 libraries as needed
- Add integration with CI/CD pipeline
- Add code coverage reporting