-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Functionality of Stream class should not be mocked by default #135
Comments
Thanks for writing! The main thing I'm hearing in what you wrote is:
I'm changing the issue title to this, and I'd welcome any info you have on how you'd expect these functions to work during testing (as I don't have much personal experience with the deeper parts of As far as the need for Stream (as well as all of the mocked classes I provide in this project) is that Arduino's libraries were written for an AVR backend and simply will not compile at all on any other architecture. As far as testing, you're right -- in a perfect world people would use dependency injection and isolate themselves from low-level components. But the Arduino ecosystem was built without testing in mind, so between the hard-coded assumption that you will have a Even the idea of mocking the low-level boards support (i.e. your target architecture is a software emulator) is problematic, because then you have to write one mock per board type. I still hope against hope that the Arduino folks will push for more than compilation tests. The Adafruit team seems to be driving that effort and "unit tests" don't seem to be in their vocabulary. I've mentioned unit testing a few times on the developer mailing list and get absolute silence in response. |
Stream
functions like timedRead
and properties like _timeout
are not mocked
The Stream works just fine if you just copy the existing original Stream.h and Stream.cpp from the Arduino core. This is what I did in my own fork. There should be no need to modify the Stream at all and it just causes problems. If you really need to mock for example the HardwareSerial do it just there and leave the Stream as it is. The non virtual functions in the Stream only use the virtual functions that are implemented in the derived classes. There is not much depencies to other Arduino code there. The project that I'm testing using this is here: https://github.com/lasselukkari/aWOT |
Stream
functions like timedRead
and properties like _timeout
are not mocked
I received some helpful information that might be relevant to this issue. At the very least, I can see that my reverse-engineering of the IDE's I think the proper thing for me to do here is take a step back and try to rebuild the mocks based on the _ correct_ set of compiler flags. @lasselukkari is that something you'd be interested in being involved with? |
I believe that the proper approach here would be to keep Also see #145 for some thoughts on further decoupling the mocking behaviour form the object being mocked. |
I've opened #146 ... I should have opened it sooner. |
The Arduino project has moved the common pieces to a shared repo: https://github.com/arduino/ArduinoCore-API. IMO these files should be taken there and left as unmodified as possible. I see that you are working hard currently to add new features. Thanks for that. But I would like to see more simplification and clean up than features. I would like to contribute to the project but unfortunately I do not agree with the general direction at the moment. I think there should be a more clear separation with the Arduino core and the testing related functionality. |
It's funny that you bring this up, because I've been looking at revisiting the entire set of C mocks. The feeling of "overcomplicating it" was exactly what I thought of when looking at an "official" In the beginning I was just creating compilation mocks as fast as I could and brute-forcing my way through compiler errors. Things like the hardware need to be mocked, but everything else can/should just use the existing libraries as much as possible! I think the only snag might be Big picture is that I need to create viable mocks for each platform. For example, what
What do you perceive that direction to be? |
Too many built in features. I may be wrong. I just quickly took a look at the commits since last spring. Personally I would be happy to implement the mocks in my own to the project I'm testing when needed instead of getting everything built in or at least that I could control when they are included. Yes, millis and micros should advance by default and it's needed that people can control the return value of the wall clock but I do no need the "godmode". I would personally only need the files from Arduino CoreApi, a test test runner, basic assertions and a way to compile the examples for selected target platforms. If everything else would be optional I would be really happy. I do understand that in the projects I'm currently testing with this it's easy to abstract the hardware away. This may not always be the case for libraries that interact with the hardware more directly. I just hope that for example I would not have to deal with the built in mocked Stream class. Instead in my own project I have this MockStream.h It is not a complete solution for all use cases but something like that would be better than directly mocking the Stream, Client or any of the other files that come from the CoreApi. I happen to have time to contribute to open source at the moment and I would be pappy to help with this project too. The problems is that in the current state I can not use this and had to do my own fork. PS. I should lear to write my comments complete first and only then hit the send button... |
I'm sorry that the journey of this project has frustrated you to the point of wanting to abandon it. I think you and I agree on the need to reduce the complexity of the mocks, and I am certainly planning to revisit a lot of my previous design decisions now that ArduinoCore-API is available (in a way that it wasn't at the time I started writing this). I would absolutely prefer to remove a good chunk of C code from this project if I could fall back on a more official version of it. The only things I want to have mocks for concern the clock and IO devices (pins, SPI, serial, etc) -- to be able to set up deterministic sequences of inputs for repeatable testing. And the only strong opinion that I have is this: for an existing library to support unit testing, it should not need to be completely rewritten (which seems only fair, since most of the popular resources on how to write libraries do not suggest using unit-testable design patterns). Arduino is aimed at a group of users who may be programming for the first time, after all. I can think of 2 possible ways to reconcile my advocacy for "godmode" with your preference to use non-mocked libraries by default and inject your own custom mocks where desired. The first option would be to allow (in the unit tests) a different The second option, which I'd prefer, (and where I'd like to hear perspectives of Arduino contributors like @matthijskooijman and @per1234) is for ArduinoCore-API to take on features that support unit testing or function mocks. In this setup, there would be some API function (or functions) exposed by Arduino that would essentially allow dependency injection for all the builtin functions. Is that kind of suggestion a non-starter? |
@ianfixes have you taken a look at the ArduinoCore-API unit tests? ArduinoCore-API is all the hardware agnostic code of the core library so it shouldn't really require much mocking. Where it is necessary, there are probably already some of the mocks written because the coverage is up to 96% now. |
I'll admit that I'm not 100% clear on how something like their |
@per1234 can you comment on my assessment of being unable to mock HardwareSerial without mocking Stream? |
The truth is that, even though I have some experience with unit tests and mocking in other contexts, I really don't understand much of what is happening in this project. I was hoping to pick up some understanding by osmosis, but I've been watching this repo for years now without getting very far. |
I can honestly say the same about the Arduino C infrastructure for boards and platforms 😉 What's the right forum where I could engage with the Arduino folks for a deeper dive on this sort of thing? |
I think probably you are already using the available communication channels so I don't have anything else to suggest. Arduino forum: Arduino users, but mostly not monitored by anyone from Arduino other than myself. Arduino developers mailing list: A subset of the 3rd party developers. Seems to be fairly well monitored by Arduino, even when not actively participating in the conversations. There is an official Discord server: https://discord.gg/jQJFwW7. I have only taken a quick look at it so I can't say whether it would be of any use to you. |
First of all, thanks for putting together this amazing project! It has been really useful for me.
Here is my situation. I have a class that inherits the Stream by wrapping another instance of a Stream. I do this so that my code works with any Stream input like Wifi, Ethernet and Serial without any changes. It also makes it compatible with anything else that expects a Stream like JSON parsers and so on. The Stream base class should the same on every Arduino. This is the key feature to write cross platform libraries for all Arduinos.
Because this project implements a non standard Stream where some of the expected functions are missing or mocked with the "GODMODE" testing anything that uses the Stream is impossible. For example, any class that is derived from the Stream should be able to access the protected Stream function
timedRead
or set the_timeout
property.I believe the Stream class should be left as it is with the real platforms. To be honest, in my opinion all the mocks for classes that are derived from Print, Stream or Client are rather pointless. Instead people should write their code to work with the base classes and for testing a mocked class that implements the virtual functions should be used instead. This is the standard practice to write testable code but now it is impossible. I feel that this decision actually encourages bad programming practises. Maybe providing mock for the HardwareSerial is useful because that is normally used directly but atleast the Stream should stay as it is.
I had to fork this project to be able to move forward. I'm fine with that. I just feel that this may cause problems for many others too and may be the wrong approach.
What are your thoughts on this? Have a nice weekend!
The text was updated successfully, but these errors were encountered: