I originally posted this post on my blog a long time ago in a galaxy far, far away. It's part of an ongoing series I've been publishing, called Uni...
For further actions, you may consider blocking this person and/or reporting abuse
I think this is truly reflect, which is one weakest point of OOP concept.
Imho, a similar reason to avoide the OOP programming as possible.
Because even with the lightest object we are attach our functions to the data, and part of these data are protected also. So handling this data is bit painfull because always need to care about lifecycle of object not just the the data.
Data with operations defined on that data is not limited to OO. For example, FP monads indistinguishable from OO classes - data with operations defined on them. The only thing that actually matters is the immutability of data. As long as data immutable, one can consider private data a "context", i.e. few more input parameters to the function.
You right. But when you attach operations to data, then you also faced to another problem, when that data is came from outer source then it need to be attach to operations, and when to send it, then need to detach from operations.
Serialization does not depend on OO or FP. There is no need to "attach" or "detach" methods either.
That's a good point Peter. Everything lives inside an object. Thanks for your comment.
Think of a normal modern JS/TS codebase. E.g. any Next JS, Solid, Vue... whatever.
You'll probably find near to zero OOP, however you'll sure come across functions that are not exported, that act just as a helper of some other function, their reason to exist is solely to help other function within the same file.
You can, conceptually, refer to them as private functions, equivalent to a private method for all intents and purposes of this context. You won't be testing these, either.
As a rule of thumb, if the only reason to have the keyword "export" in a function is so you can import it for testing purposes, you're doing something wrong.
Easy way to explain it is that we don't test steps (functions/methods), we test the rigour of the algorithms (input A = output B) to ensure our software keeps working as expected, wether the algorithm has 2 or 95 steps is none of our business from the testing perspective.
This explanation is just to show an example of the same situation without involving the programming paradigm to avoid getting ourselves into the weeds here. These are two completely separated discussions.
Don't forget the WebComponent where you need to be use OOP pattern.
But what is the deep concern of private method in OOP? Maybe we would like to show just a minimal public interface to the outer word, and protect that function to called from out side. So technically enough to test the public part of our Object. Under Object development maybe we set protected method to public, so we can write a test for that. Later if our object are stable, we can move that part to protect. Or we can use Symbol key for that function which we don't want to use public, but these Symbol we share to test, but not for in our lib.
The method of Schrödinger 😂😂 No, don't do that, it just makes things more complicated for no reason. If you test all use-cases of your public functions/methods you're implicitly testing all the private ones at the same time and if not, the coverage will tell you exactly what you are missing.
Lastly I can't honestly consider web-components as part of the "modern" ecosystem. They're a standard API, yes, but a bad one nevertheless.
If you don't use framework, then WebComponent will be really handy. I created a markdown-viewer WebComponet which is part of my game development process, and that is fine. I try to skip using JS framework. Maybe check out my game development: dev.to/pengeszikra/javascript-grea...
But... why? There are JS frameworks literally created with game development in mind. Phaser, Pixi, Babylon, PlayCanvas... Or even Three.js if you want to toy around at a lower level.
If it's for learning purposes it's fine but if you're serious with your development and want to achieve a production-ready product... Why reinvent the wheel?
I think the most misleading point for developers is how they interpret the term "unit" in unit testing.
A unit is a "unit of work" and does not necessarily have to be a method in a class. Instead, a unit should be seen as a single behavior of a component towards the external world (in other words, its public interface).
Moreover, if the component adheres to the Single Responsibility Principle, writing tests is straightforward in most cases.
Exaclty! Unit of Work != A single method
Determining if the component adheres to SRP is not straightforward, though. Much easier and at least as efficient is to follow the Single Level of Abstraction approach.
Yeah, while I understand what you're saying, testing private methods only via their usage in other methods is poor practice IMO as it creates a paradigm where it is truly impossible to test all possible cases.
Especially in an paradigm which already encourages nearly-infinite code complexity, burying code in private methods and accepting that it will never be completely tested seems like malpractice.
In my opinion, if you have a private method, and you can't test some part of that method from the public methods that call it, then you're not using that part of the method, and it should be removed.
There's not really a reason you wouldn't be able to test each part.
I second this :)
The part I think you’re missing is that those private methods are themselves their own function. In order to test the private methods completely, you’d need to have all possible conditions available in your public functions, and your tests would need to cover all conditions in both the public and private methods. The cyclomatic complexity can get pretty nasty pretty fast.
Another point against OOP, I guess.
All possible conditions should be available from your public functions. If they're not, then those conditions are not possible, and can be removed from your function.
E.g., let's say I have something like this:
In this example, the try/catch can't be tested, because
divide
already checks for a zero. Therefore, thetry/catch
is unnecessary. There is no code that calls it that can use it. So, you can leave it out, and change the second method simply to:Now, there's no untestable code, because you got rid of the unnecessary code.
If you have two tests against a public method and two tests against a private one, and you change it so you instead test the private behavior through the public function, you still have a grand total of 4 tests. The quantity of tests don't have to increase because you're testing behaviors through the public API.
Also, if you really are trying to test each private function independently in order to have a low cyclomatic complexity, so you can fully test each thing, well, that doesn't really buy you anything. If you really think about it, every single operation and piece of syntax you use in the language is well tested - all you're doing is integrating those pieces together. But all of your bugs are going to lie in the integration. Similarly, if your unit of test is as small as individual private functions, well, each of those private functions will work wonderfully, but you're going to have lots of bugs in how they integrate unless you test larger chunks at once, even if they have high cyclomatic complexity.
Not understanding the purpose of the code might be a reason to test every function. I'm not justifying it, but I could see that as a path of least resistance to ensure 'everything is tested', without the perceived overhead of designing comprehensive tests that cover every scenario.
If you can't reach all of your private methods from your public methods for testing, then you either have dead code, or need to reconsider the purposes of your methods.
This is why code coverage should be an essential part of unit testing. It enables to inspect those private parts and whether tests covered them, too.
This is just a problem with legacy programming languages. In modern languages (like Rust) you can access private identifiers in tests without breaking encapsulation. If a language forces you to avoid testing a part of your codebase simply because it has design flaws, this is a reason to don't use it in future.
Thank, you but no. I will test my private internal methods without breaking access rights.
You can easily in modern c# use includes to import your test units as if they were private and internal to the class being tested without breaking encapsulation.
Thanks for sharing.. I think that we test fist behaviour, not implementation details. If private methods should be test, we must consider whether they should be extracted into their own class or exposed through a public interface.
Testing private methods indirectly, via assertions about the result or behavior observed from public method calls, also provides the advantage of plasticity. You can refactor your "internal" logic (private bits) and, provided you have thorough testing of the public methods, and they pass, your refactor will go much smoother with less coupled/spider-web/entangled changes. If the output/behavior doesn't change, from the perspective of calling a public function, it shouldn't matter how many or what specific private methods are called.
That being said, I prefer Rusts capability for testing "private" functions. You get the best of both worlds. Your "higher level" tests fail when "lower level" code fails but you get the specificity of what "lower level" is causing your failures without bashing your head against a wall chasing down your call chain. You simply write a test like any other and as long as the "private" method is in scope, it just works. No need for any workarounds, indirection or monkeypatching (looking at you JS/TS).
Hmm.
The way I read this article, the most prominent argument is: We don't test private methods **because* they are private.*
IMO this explanation does not give any reason at all.
For me, one reason to test private methods/functions is to make it easier to reason about them.
I want to write (and read) tests, that explain to me how a function behaves.
When a class transforms one complex state into another, I find I way too complicated to craft (and later read) the exact input states that lead to the expected output, where all edge-cases are covered. Instead I prefer to test the edge-cases at the private methods.
Whenever it's possible, I try to avoid testing private methods. But from time to time, It makes my code much easier to understand. And that is the second most important thing after writing code that does what it is supposed to do.
BTW: I wrote an article on how to test private methods in TypeScript.
Your blog post really sparked my curiosity! I had never known that this topic could be this vast and informative. As we are working on Garnet ring, we want some information and your suggestion on this topic. For a brief detail what we are into, please visit out website. We will be waiting for your new blog and your feedback for us.