-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Proposal: mocking support #500
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
Comments
I think the whole concept of mocking is an artifact of the OOP philosophy which seems to me at odds with the spirit of what zig is trying to be (please correct me if I'm wrong). If you are writing low level code I would think you are mostly thinking about data and how to manipulate it. So if you want to make sure your code is testable and reliable, structure it so that the data manipulation is "pure" in that it's independent of any input/output from the environment. When you are dealing with pure data, there's nothing to "mock". You just create sample data and feed it to the function to test its output. For example, if you have to parse csv files and need to test your parser, write it so that it receives a string representing the content of the csv file or a line from it, not a file name that it needs to open. Then you can easily write a test on the function because you just pass it a string and there's no Input/Output to mock. |
@hasenj: I don't think mocking is only OOP thing, it just happened that OOP turned testing (also) into giant monster. Secondly, it is not always possible (or desirable) have pure functional design which would (mostly) not need such a feature. Say you have the most primitive webserver (could be done in 50 lines of C) and want to test it. Instead of adding many lines which hijack functions like What you get:
|
I'm not so sure whether we would need a separate mocking support instead of just having more advanced facilities for compile time type manipulation. Basically after initial implementation for your mockable type builder function you could just redefine an interface as you see fit. This of course has some implications such as having to know what you might want to mock and you would have to program against that interface. However as a possible improvement to @PavelVozenilek's suggestion. Instead of using a re-define keyword we'd use the test keyword to redefine functions in test scopes.
|
Here's idea how it could be implemented:
Notes:
|
If a user needs to mock a function, can't they implement this indirection themselves? Instead of calling |
@hasenj: that's complicating the design to support testing. In extreme this leads to complex mock frameworks, everything being interface, everything being publicly accessible. Imagine testing hundred of functions. That would be hundred of flags and three hundred of new names (my_open, my_mock_open, the flag). With mock proposal above it would be 0 flags, 0 new names and no design changes. You delete a test and no other fix is needed. |
@PavelVozenilek, I certainly have used some mocking frameworks in C in the past, but they were very, very limited in scope and used ugly macro tricks. It has been many years so I do not remember details. Much of it relies on LD_LIBRARY_PATH and other hacks. Since Zig is compiling everything from scratch, I am not sure that you need to do the patching. @hasenj, @Ilariel, I would see this as possibly extending beyond the use of mocking for tests. The LD_LIBRARY_PATH trick allows some very interesting (if somewhat delicate) replacement to happen. Perhaps something like:
Here I used a new keyword Any kind of mocking or function replacement is fairly useless if you need to be able to modify the original code to make it work. Perhaps there is a way to leverage compile time to get most of this?
Then in the rest of the code in that module when you use |
@kyle-github, I have to agree that conditional compiling with compile time constants is good enough for most cases and it has always been good enough for my usage as I usually need proper logging data and small tests aren't exactly good enough for that big things. As a disclaimer I also have to say that I haven't looked at the compiler code or written any tests in Zig. However given that tests should be their own isolated cases of code which should fail when result isn't what it should be and they aren't exactly something that you export as library code or as real executables I think function/constant replacement in the scope of the tests with |
I'm confused with the status of this request. It is closed as completed and has a milestone associated with it, yet seemingly no changes has been made (or I just wasn't able to locate corresponding commit) and there is no description on how the mocking is supposed to work. None of suggested keywords (override/test/re-define) works and there is no mentioning of this in the 0.6.0 release note or language reference. Was it indeed implemented or just closed without any resolution? |
GitHub didn't add "completed" vs "not planned" issue closures until fairly recently; all previously closed issues were assumed to be "completed". Any closed issue labeled "proposal" but not "accepted" is a rejected proposal. |
Definition from Wikipedia:
When one tries to do mocking in C++ he usually needs to:
Something what on first sight looks rather simple turns into nightmare. Design needs changes to allow mocking, internal details have to be made public, complex frameworks are invented to support mocking.
My proposal: if a test needs to mock something just redefine that thing inside the test.
Whenever compiler sees a test containing redefinitions it acts as if original functions do not exist anymore and uses redefinitions instead. For code outside this specific test nothing changes.
(I suppose that names are unique in Zig, so no redefinition will be ambiguous.)
Original functionality may still be accessed:
Redefined functions may access test local variables as "globals", to communicate intent and results. I believe access to parent's locals is planned feature for inner functions, this is similar.
Unit test may need imports to do the redefinitions:
Anything could be mocked, e.g. constants, struct methods, types ...
Implementation: I am not sure whether such feature could be easily implemented. One (not very scalable) way is to do complete project recompilation for every single test with redefinitions, doing replacement on source code level.
Situations for which suggested feature is not appropriate: very complicated mocks that would require lot of redefinitions. These situations would be better handled in traditional way, with design changes.
The text was updated successfully, but these errors were encountered: