Native Testing Setup for MyStation
Overviewβ
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.
Setup Componentsβ
1. PlatformIO Configuration (platformio.ini)β
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 = off
2. Mock Files Structureβ
All 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
3. Mock Implementationsβ
Arduino String Class (esp32_mocks.h)β
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()
ESP32 Logging (esp32_mocks.h)β
Mock logging macros that output to stdout:
ESP_LOGI,ESP_LOGW,ESP_LOGE,ESP_LOGD,ESP_LOGVRTC_DATA_ATTR(no-op for native)
Preferences Library (Preferences.h)β
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.
ConfigManager (config_manager.cpp)β
A simplified mock implementation that:
- Defines
rtcConfigwith default values - Implements all public methods
- Uses
#ifdef NATIVE_TESTto compile only for native tests
4. Conditional Compilation in Production Codeβ
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
#endif
5. Test Structureβ
Tests 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
Running Testsβ
Run all native tests:β
pio test -e native
Run with verbose output:β
pio test -e native -v
Run with extra verbose output:β
pio test -e native -vvv
Test Output Exampleβ
[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
6 Tests 0 Failures 0 Ignored
OK
Key Benefitsβ
- 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
Adding New Testsβ
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
Troubleshootingβ
"Undefined symbols for architecture"β
- Check that source files are included in
build_src_filter - Verify mock implementations exist for all dependencies
"file not found" errorsβ
- Ensure mock headers are in
test/mocks/ - Check that
-Itest/mocksis in build_flags - Verify conditional includes in source files
Framework inheritance issuesβ
- Make sure
framework =is explicitly set in[env:native] - Set
lib_deps =to override inherited dependencies
Future Improvementsβ
- Add more comprehensive test cases
- Mock additional ESP32 libraries as needed
- Add integration with CI/CD pipeline
- Add code coverage reporting