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 Unit Testing 101.
Trying to test private methods causes a lot of confusion.
That's a common question we all have made when finding unit testing for the first time. These days, I found that very same question on Reddit:
Can someone explain to me why unit testing our private methods is bad?
And here's my full answer:
Because we don't want to break encapsulation.
If you have a private method, how are you going to call it from a test class or method?
It's private. You can only access it from inside the same class. That's the whole point of access modifiers: restricting access to the fields and properties that hold the internal state of a class.
And please don't make your private methods public and static to call them directly inside unit tests. They're private for a reason, right? We don't want the rest of your code to use them directly.
Exposing internals is the most common mistake when writing tests. I've seen it and fixed it before.
Let's take the HasAdmin()
method from the question as an example,
private bool HasAdmin(List<string> relations, bool hasPermission)
{
// Beep, beep, boop...
}
Unless HasAdmin()
has 0 references—if that's the case, you should remove it—another method from the same class is calling it. And you can trace the chain of method calls back to a public method.
HasAdmin()
, or any other private method down in the chain of method calls, is changing something that you can observe from public methods. Probably it's affecting a return value or changing an internal state you can inspect with getters. That's what you should test instead.
To test HasAdmin()
, create a User
object with the right relations and permissions, call the public methods you have, and check what should change when your user is an admin or not. Maybe you return additional data only admins can access or finish an action without throwing a UnauthorizedAccessException
.
You test private methods indirectly while testing the observable behavior exposed through public methods.
Et voilà!
Download your free copy of my ebook, Unit Testing 101: From Zero to Your First Tests. It covers all the basics to help you write your first unit tests in C#. Plus, get 5 bonus lessons delivered straight to your email to make your first tests even better.
Top comments (45)
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.
Real world objects have both functionality (you can tell it to do something by calling one of its functions) and state (it stores data as attributes and relationships to remember what state it is in).
Real world objects also have lifecycles - we can try to simplify an application by pretending that real world objects don't have lifecycles but then you would be breaking Einstein's simplicity theorem which goes something like "Make things as simple as possible but simpler" i.e. not too simple that it's no longer correct!
Assuming objects are 100% immutable may satisfy our puritanical mathematical desires and fit nicely into "set theory" but it's just a theory that's best kept for the theoretical mathematics lectures at university - meanwhile, back in the real world, we need to cope with the fact that objects aren't always immutable and so we need to be able to write software that can represent thousands or millions of real world mutable objects and the interactions between them.
OOP allows you to encapsulate the functionality and the state of a real world object inside a software object that is defined by a class. It also allows incredible amount of reuse via inheritance (making sure, of course, that the correct balance of inheritance vs composition is always achieved).
In other words OOP is the most programatically efficient way to code if you want your software objects to be as close as possible to the real world objects that they represent.
Many developers love this concept and have discovered that OOP's ability to model real world objects via encapsulation, inheritance and polymorphism means that as their applications grow the complexity of the source code grows close to linearly instead of exponentially as with most other paradigms that came before and after it.
It is sounds good, but at the end of the day we are combined and inherit of some class, where we are hope ( have hidden part ) to solve our problem, but in a real world object never same as the picked one. Or in other example: we just want to make a documentation with code example, and some tricky reason - company are mandatory use excel and word for work. So we pick a word to make our document. Which is unreadable ( don't count how professional we are on word use ) because word separated our document to unified page sized, because that program is depend on old print to something to paper. Now we are try to save our planet and don't use paper for every uninformative documentation ( I hope ), but we are still using word. Why? because we just want a banane but get the whole jungle.
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 am big fun of Mr. Doob. But Three.js for example are created 2010 so that is based on OOP pradigm, Phaser also. Each of these game engine are based on WebGL. But my initial ida is: the simple vanila HTML/CSS is much more useful to represent card game in 3D. Because on WebGL really hard to make a well formated text on 3D plane. Also problematic to make a simple card which rounded corner.
Please show me another card game, where you saw the deck as real 3D object maded by cards.
So that why I spend work to create a this way.
The state handling is also important to every complex game flow handling. My previous game used by react have much harder state management, compared to this one. Where I can use a one simple function with Proxy to handle my states, and that is working perfectly, because that state don't attach directly to a view as in React. So I can use my code even on server side also.
My last goal is to compare how large or small code/css need to be make for creating a this type of game.
Summary: I would like to pushing forward our way of working and thinking.
Please do create a new post with your discoveries and comparisons, I am looking forward to reading them all! 😁
thx, I will
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.
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.
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.
I second this :)
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.
The public interface should test all cases. That's what unit tests are for, not private methods.
Private methods should be tested through development and subsequently, by proxy, via the public interface. If you can't test all cases of your private implementation via public interface, then you have a different problem, either:
Not all cases for a private method may even come up. If you have a public interface which say, coalesces null values to a default value, for instance, then it would be pointless (through unit testing) to test a private method to see what it does when it receives a null value, as it will never realistically receive one via the public interface and that's why you only ever test the public interface.
No one is saying that private methods should not be tested. However, their testing is not what unit testing is for. If you're testing private methods using unit testing frameworks, then you've gone VERY awry.
Not sure if you're categorizing private methods as some sort of special category of code that never changes or has bugs that are only discovered a year after release, but why would they benefit any less from test coverage than any other block of code you write?
They're not a unit in the scope of unit testing. It's as simple as that. If you can't test private methods via the public interface, then your code is most likely not structured correctly or you haven't written your tests correctly, or it's a perception problem.
If a private method changes, then it would have an effect on the public interface, which would then fail the public interface test. If it doesn't, then it's either a) not a problem, as the unit still works as expected regardless of the change, or there's a problem with the test, which is outside the scope of what we're talking about here.
Unit testing is for units. Private methods are simply part of a unit's implementation. They simply do not need to be tested via unit tests explicitly. They will be tested implicitly via the public interface of the unit test.
And, as I've stated already, if feel like you need to unit test a private method, then you're then either a) being overly sensitive about "coverage", or b) you should expand your public interface test to include coverage for the scenarios you wish to test, that will hit the private method, or c) it's a code smell and should point you toward tree-shaking or refactoring of the unit itself.
The real "debate" here is what constitutes a "unit" and that to me is very clear. It's a black box with a public interface. Now, of course, you could argue that a private method is just that, but it's, quite simply put, overkill. Test coverage should be implemented via a public interface and code corrected accordingly according to the nature of that testing. If you feel you need to test private methods, then you shouldn't respond with "I need to test private methods". You should respond with "where have I gone wrong and what can I do to correct the tests/code to satisfy what I'm seeing from the unit tests".
I second this chain of thoughts.
Agreed. For my teams, we use interfaces exclusively for encapsulation and mark methods public for testability. Then again we're not really using oop. As far as I've seen, that's for the best.
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.
I was going to point this out as a rebuttal to this article, but since you already pointed it out, I will throw my hat into this ring as well. My way of viewing the problem is that public interfaces are for teams using your services through your team's API, but your team should be able to test whatever code is needed to be tested and private testing usually devolves into a lot of mocking and now you have to ensure that those mocks are updated anytime the signature of the function/method changes, which happens more frequently since it isn't exposed to anyone outside of the team and isn't considered a contract between consumers and providers.
I find it ridiculous to limit developers working on a code base to the same standards as those that are using the services it provides. I would caution that you must have tests for the expected behaviors of the public interfaces and that these tests should cover all scenarios that are documented, which often is a much shorter list than the business logic needed to create the output.
Agreed. My private methods often have complex business logic, and you can bet I will want really thorough test coverage on it.
Didn't know that about Rest. Thanks for sharing!
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.
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).
Wow! This is the second time I read in this thread about Rust and testing private functions...I'm curious now. Thanks for your comment.
I guess this is a "it depends" type discussion.
I think the purpose of unit testing is to ensure that a planned and necessary behavior works as we define it. It's quite possible that there are some bugs even with unit testing because we probably don't consider all real scenarios, but we can cover them if they appear.
That said, I also think that some parts of the code don't need to be tested, only the exposed functions, only the "views" (internal APIs, helper functions, application services, infrastructure services)
E. G.
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.
I understand your point, but I think the testing you describe sounds closer to an integration test than a unit test.
Let's say that I have a public function that returns a list of users, and it uses a private function that implements a custom, efficient sorting algorithm specifically tuned for this use case.
There are a lot of edge cases that I might want to test for my sorting algorithm. But it's really not necessary to test them through the public interface, which might do a lot more other work (eg, database fetching).
Yes, I can do all this testing without breaking encapsulation. But writing the tests through purely public interfaces makes the tests more verbose, slower, and less targeted - it's slightly less clear what exactly I'm trying to exercise and test.
In this situation, I think testing the private function directly is better. You might argue that it shouldn't be a private function at all, but why not? If it's a highly specialized algorithm that will never be reused elsewhere, it should be private.
Ultimately, the real problem isn't that we shouldn't ever test private functions. Nor is it a problem with OOP (another comment described un-exported functions in typescript that are like "private" functions. It's that we can't test private functions in a language like Java.
Cool to see examples of other languages that allow this (eg, Rust, .net).
One more case: some rpg game, public getMagicDefence(u: unit) -> u64 which return sum of protected fromStats(u: unit) -> u64 and protected fromPerks(u: unit) -> u64. In test I create some unit and call getMagicDefence and get 10. Expected result also 10, so its looks good. But fromStats returns 9 and fromPerks return 1 when correct result must be 6 and 4.
And what about TDD?)
About Rust: write tests in child module which declared in same file or other file placed in same or child folder. Child modules can call anything from their parents. Read more in official rust book: doc.rust-lang.org/book/ch11-03-tes...
Also one more great thing in Rust is documentation tests. Just hover cursor to imported function and see signature, description and short actual unit test.
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.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.