This project offers an interface for buzzers, and I am not sure how to test most of its functionalities. The public methods are very high level in my opinion and I cannot seem to find the right way to design unit test methods for them:
void init() returns nothing and initiates private members. I dont see what I can test here
step() will step through a private FSM state and play the specified melody, one note at a time. This is where most of the stuff happens, but everything is wrapped in this single public high level function and from my understanding, unit tests should not be designed for private members, so how can I test my step function, what are the best ways to do so?
What's your understanding of "unit tests" with microcontrollers?
Typical unit tests check the value returned from a test program.
How would you make an Arduino sketch return a value?
Which values should be applicable to a buzzer?
How do you determine the value inside the sketch?
I am not sure if those questions are rhetorical, but using platformIO you do not work on a .ino sketch, but on more traditional .cpp and .h files. There already is support for unit tests within a test folder inside every platformio project folder. All there is to do is to create a test.cpp file(s) that contain proper coding tests. You can consult their doc Redirecting...
I find these questions challenging. Given my step() function is void, I should create a few getters and check the state of the FSM, the melody being played, ... have indeed changed after stepping through the state machine. That would seem like a good approach?
I still can't see how emulators or unit tests can provide meaningful results in a real time environment.
Which possible errors do you intend to find how in your code?
In case of a state machine, do you want to check each state transition under all possible circumstances, in all possible sequences of events?
Have you ever found an existing unit test for any state machine?
So in your opinion unit tests are not required in this firmware module? I was expecting to test that the melody plays each note for the right amount of time, completes properly, etc. I thought it was a good thing to write unit tests for all code modules I would design in the future. When would firmware modules be pertinent vs when would they be useless?
I seem to be able to find articles on designing state machine unit tests, so I would guess this practice may be a good thing?
IMO such tests are quite useless as soon as complex (controller) hardware is involved.
IMO unit tests are used to detect regression when a known (old) bug occurs in a new software revision again. Such procedures are essential e.g. for compiler evolution. In µC this would mean that you get an error report, write test code and data sets to detect that error in the current code, then fix it and check that the test now succeeds. Let all unit tests run after every new unit version or at least whenever errors are suspected.
Another challenge related to writing the unit tests is the difficulty of setting up realistic and useful tests. It is necessary to create relevant initial conditions so the part of the application being tested behaves like part of the complete system. If these initial conditions are not set correctly, the test will not be exercising the code in a realistic context, which diminishes the value and accuracy of unit test results.
A µC has a lot of signal processing hardware in it (timers, ADC...) that is already used by the framework (for timing). If a software unit uses part of that hardware that is also used by other units then it's useless to test both units independently, because they probably can not cooperate. Or it's only the computation time of one unit that prevents another unit from working properly. µC coders face such concurrency problems every day, and no single unit test can help them out