matklad replies in the thread to clarify the testing argument
4 Nov 1 7:58 AM · 10d ago · 4 comments · 1 source · development 4 of 4
Responding to a sub-thread questioning what 'mock your IO' means, matklad elaborates that there are two useful cuts in an application: the thin waist connecting the app to the external world, and a separate cut for testing the implementation of that waist itself.
“That's a good observation, that there are many different ways to cut the interfaces such that pure business is on the one side, and side-effecting syscalls are on the other. And any cut works!”
matkladmatklad Blog post authorandrewrk Lobsters commenterazimux Lobsters commenterdavid_chisnall Lobsters commenter
The whole story postscomments the bright band is this development · numbered dots are the others · click one to jump
What people said 15 voices · verbatim
-
I’m not sure I agree with this. I tend to build public APIs in layers. The low layer public API exposes all necessary functionality. The higher layers expose *convenient* functionality for common use cases. In C/C++, the higher-level interfaces are often header-only and designed to be inlined in the caller. They also serve as documentation: if you…
-
OP did say it’s OK to have helper functions that call _only_ other public functions.
-
Instead of reading my comment, you should watch Gary Bernhardt's superb talk "Boundaries", which discusses this very question. https://www.destroyallsoftware.com/talks/boundaries But I'll try to do a very brief answer to only your question. Your question sounds like it assumes a code structure that looks like this ... ``` def my_procedure()…
-
That's a good observation, that there are many different ways to cut the interfaces such that pure business is on the one side, and side-effecting syscalls are on the other. And any cut works! But I think ideally you actually want two: * Most importantly, you cut the thinnest waist that connects your app to the external world (the trick here is…
-
Recursive mutexes exist for pretty much exactly this reason. They are the easiest way to make a non-thread-safe library thread safe: every public API call grabs the mutex and releases it on return. They’re not quite as efficient as non-recursive ones, but they’re not that expensive: typically the recursion check is done on the failure case: try to…
-
This is especially true when some internal state is guarded by a mutex. In that situation there’s the danger of a function holding the mutex calling another function that acquires the mutex, causing deadlock. If you have a rule that public functions grab the mutex and internal functions can assume the mutex is held, _and_ the class never calls its…
-
It depends: for example, in Rust's `Vec` there are a lot of pub methods that call other pub methods for a bunch of different reasons (safety, reuse, allocator API, ..).
-
My tuppence worth: if foo() can be implemented in terms of bar(), then that suggests the API isn’t well factored. Maybe one of them is at a different level of abstraction? I note that features like Java’s default methods on interfaces encourage public methods to depend on other public methods, because that is all that is visible.
-
IME and IMO if you have to worry about this at all it is a sign something else is wrong with your design. Maybe you shouldn't have public member functions for this at all. Maybe you should be using a more functional design. Maybe you should be using a less OO and more procedural design. Something is wrong.
-
A place where I learned about this style and that was useful is https://sans-io.readthedocs.io/. It's targeted at networking protocols but you can usually extend it to any IO imo.
-
Thanks for clarifying! A couple more questions: > the trick here is architecting your application in a way that creates thin waist. Eg, you can have MyCustomeResourceStore, our you can can sprinkle raw SQL calls all other the place Should I interpret this as “both of these are examples of a thin waist” or is one meant to be thicker than the other?…
-
I read it the same as you and what I usually do is exercise all exceptions/faults indeed to make sure they're handled properly. It's sometimes difficult to put the system in a case to cause those issues while patching/mocking is a trivial one-liner (at least in Python which I'm used to).
-
That actually addresses the problem. The way that you capture a source location in C++ is to have a source location as a default-initialised parameter. If you have two public functions that do this, the one that calls the other just passes the source location rather than relying on the implicit value, so the reported source location will be the…
-
But e.g. you wouldn't want to implement push by calling reserve, because the implementation of push already needs the branch to check whether it needs to grow, which reserve would duplicate.
-
Similar, in C++, the public function captures the callers source location and passes it around the implementation.
All 4 developments of matklad's 'don't let public functions call public… →
Hacker NewsLobsters