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