That topic might by a bit more complicated than you have anticipated.
The first error stems from the LDF rejecting the full inclusion of the library because it sees that it needs framework = arduino
or mbed which is not set in the native environment, since there is no Arduino there normally. That is worked around by adding lib_compat_mode = off
.
Next, you will see that even if you only include the IoAbstraction.h
header, it will still ask for the Arduino headers in this library and its dependency.
Processing test_common in native environment
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Building…
In file included from .pio\libdeps\native\TaskManagerIO\src\TaskManagerIO.cpp:6:
.pio\libdeps\native\TaskManagerIO\src\TaskPlatformDeps.h:201:10: fatal error: Arduino.h: No such file or directory
…
.pio\libdeps\native\IoAbstraction\src/PlatformDetermination.h:33:10: fatal error: Arduino.h: No such file or directory
Etc. So while code for mocking exists natively in the library, it’s designed for unit tests running on the embedded device where it still has the Arduino core available to compile the rest of the library. Not pure desktop.
If you want exactly that and no desktop tests for that, you should be able to use it normally in the embedded unit tests.
However, there are still tricks to be done for native. By using ArduinoFake, we get a mockable version of the Arduino core, on top of the FakeIt framework and will provide the Arduino.h
header.
This is also used in PlatformIO in the example platformio-examples/unit-testing/arduino-mock at develop · platformio/platformio-examples · GitHub.
Using that we can compile the “Arduino Framework” for native and setup the unit tests to make the Arduino core functions return what we want when our business logic calls them, and then test the results of th business logic.
However, when using this on top of IoAbstraction.h, we still get a fail: It won’t be able to find the Wire
library or Wire.h
. This is missing in the current version.
Processing test_common in native environment
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Building…
In file included from .pio\libdeps\native\IoAbstraction\src\EepromAbstractionWire.cpp:7:
.pio\libdeps\native\IoAbstraction\src/EepromAbstractionWire.h:38:10: fatal error: Wire.h: No such file or directory
But, using a bit of looking around and figuring out the lib yourself, one is able to come up with the missing Wire mocks. After some fixup for the min
macro, the IoAbsraction library also compiles natively on desktop then.
I’ve created the repository GitHub - maxgerhardt/pio-unit-test-mock-ioabstraction: PlatformIO example using ArduinoFake and IoAbstraction for unit testing on the desktop with a test example that compiles for desktop and uses the IOabstraction library. First it uses the direct mock, then it gives a small but complet business logic test using a switch.
The technique there is that the business logic accepts that you can set the IoAbstractionRef
. Usually this is your e.g. I2C expander device object or something, but it can be substituted with a MockedIoAbstraction
. Hence we can simulate some input / stimulus and check the reaction of the business logic.
The IoAbstraction library can also be mocked in a more “hardcore” way. Using the expanded ArduinoFake library, we can let the IoAbstraction library think and use a “I2C expander” via the Wire library. We can then mock the return values and behavior of the Wire.xyz()
calls. This is like simulating what I2C data is read over the I2C bus, at that level. Then the IoAbstraction library handles that as normally and as if it got it from a real I2C device.
That way is however really complicated, since you must now know what I2C packet data to return at which exact points. If one just cares about whether the business logic gets some input stimuli, the way above is way easier. But if the reaction of the IoAbstraction library and your business logic is supposed to be tested against abitrary I2C packets on the bus, that must be used. But that’s then also kinda like testing the IoAbstraction itself which I assume is already well-tested.
An example of just a mock-test for Wire is also included.
Processing test_desktop in native environment
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Building...
Testing...
test\test_desktop\test_calculator.cpp:192:test_function_calculator_addition [PASSED]
test\test_desktop\test_calculator.cpp:193:test_function_calculator_subtraction [PASSED]
test\test_desktop\test_calculator.cpp:194:test_function_calculator_multiplication [PASSED]
test\test_desktop\test_calculator.cpp:195:test_ioabstraction_mock [PASSED]
test\test_desktop\test_calculator.cpp:196:test_switchinput_mock [PASSED]
test\test_desktop\test_calculator.cpp:197:test_simple_arduino_mock [PASSED]
test\test_desktop\test_calculator.cpp:198:test_i2c_wire_mock [PASSED]
-----------------------
7 Tests 0 Failures 0 Ignored
OK
===================== [PASSED] Took 7.48 seconds =====================
Test Environment Status Duration
------------ ------------- -------- ------------
test_common native PASSED 00:00:01.468
test_desktop native PASSED 00:00:07.476
==================== 2 succeeded in 00:00:08.943 ====================