Classes don't encapsulate like they used to
There is a new focus on interfaces in TDD (mostly to make mocking possible). I had noted to several coworkers that it seems that classes don't encapsulate like they used to. Everything has to be accessible in order to be tested, and that pollutes the namespace of the class. Yes, of course that's true, said David Chelimsky, but the encapsulation is done by the interface these days. The interface is clean, and the class itself can be exposed. After all, the "real code" (my term, not his) will see the class only through the interface, but the tests can look at the concrete class directly. And of course, he is right about this.
!commentForm
Why should the tests need to look into the class itself? If the real code doesn't need to, the tests shouldn't need to either.
The tests are a specifications for the class. They document how it works, how it should be used. Do you really mean your tests do document that it's alright to sidestep the encapsulation?
The tests are a specifications for the class. They document how it works, how it should be used. Do you really mean your tests do document that it's alright to sidestep the encapsulation?
There is a difference between invoking something and verify the invokation worked. I'll say real code should just do the invokation and expect it to work.
When testing we may need to query the object for the information we need to verify success or failure.
When testing we may need to query the object for the information we need to verify success or failure.
In *black box* testing, tests should not access the inner features of the class, or better, of the service (hence the interface).
In *white box* testing however, it is just the opposite: for example, if we want to test that a certain method is using some lazy pattern appropriately, we might want to know whether the operation was really completed on demand or not, if it is performed redundantly etc.
In *white box* testing however, it is just the opposite: for example, if we want to test that a certain method is using some lazy pattern appropriately, we might want to know whether the operation was really completed on demand or not, if it is performed redundantly etc.
Isn't TDD always black box testing? After all, you haven't written the code to make the test pass yet.
When you've got a real simple idea like a class, and they you see smart people adding features like interfaces, you begin to wonder if maybe H. L. Mecklin was right: "For every complex problem, there is an answer that's simple, obvious, ... and wrong."
If you feel you need to test a class's private workings, maybe, just maybe, they should be another class? Testing the internal working of a class means you've just coupled the tests to the specific implementation, and you're going to have to rip them all out and rewrite them if you decide to change the implementation.
John Roth
When you've got a real simple idea like a class, and they you see smart people adding features like interfaces, you begin to wonder if maybe H. L. Mecklin was right: "For every complex problem, there is an answer that's simple, obvious, ... and wrong."
If you feel you need to test a class's private workings, maybe, just maybe, they should be another class? Testing the internal working of a class means you've just coupled the tests to the specific implementation, and you're going to have to rip them all out and rewrite them if you decide to change the implementation.
John Roth
hmm interesting.. we've had this sort of debate going on in our team for a while.
We have classes that use interfaces (instead of concrete references) so that we can mock them.
We have unit and acceptance tests for the public APIs/methods of our classes.
Then we have private methods in those classes...
...
...and I don't think anyone is really confortable with them.
As you point out, I think we are struggling with the tension between making everything public (or maybe internal, since we're working in C# and we could put our unit tests in the same namespace as our code) for testability, and the 'classic' concept of encapsulation.
Right now, my personal take is that:
1~ the only good reason to have private methods is that you refactored some code from a public method
2~ therefore, your tests for the public methods should cover the private methods as well; no need to turn them public
I think this owrks. Sure, it puts YAR (Yet Another Responsibility) on the developers (namely: make sure you and your team-mates are following guideline #1), but that's ok.. that's part of wearing that green bracelete, I think.
F.O.R.
We have classes that use interfaces (instead of concrete references) so that we can mock them.
We have unit and acceptance tests for the public APIs/methods of our classes.
Then we have private methods in those classes...
...
...and I don't think anyone is really confortable with them.
As you point out, I think we are struggling with the tension between making everything public (or maybe internal, since we're working in C# and we could put our unit tests in the same namespace as our code) for testability, and the 'classic' concept of encapsulation.
Right now, my personal take is that:
1~ the only good reason to have private methods is that you refactored some code from a public method
2~ therefore, your tests for the public methods should cover the private methods as well; no need to turn them public
I think this owrks. Sure, it puts YAR (Yet Another Responsibility) on the developers (namely: make sure you and your team-mates are following guideline #1), but that's ok.. that's part of wearing that green bracelete, I think.
F.O.R.
"encapsulation is done by the interface these days"????????
I said that?????? Tim, you must be confusing me with the David Chelimsky that sells real estate in Vegas (there is one - really).
Seriously - that statement goes against a lot of what I believe, so I can't imagine having said it exactly like that, with that much generality. I think that it's generally bad to expose internals - not just to tests, but to "real code" ( ;) ) as well. As with any guideline, I'll make exceptions if the tradeoff provides some benefit (like testability), but if there's a way to get that benefit without pulling my pants down, that would generally be my preference.
I said that?????? Tim, you must be confusing me with the David Chelimsky that sells real estate in Vegas (there is one - really).
Seriously - that statement goes against a lot of what I believe, so I can't imagine having said it exactly like that, with that much generality. I think that it's generally bad to expose internals - not just to tests, but to "real code" ( ;) ) as well. As with any guideline, I'll make exceptions if the tradeoff provides some benefit (like testability), but if there's a way to get that benefit without pulling my pants down, that would generally be my preference.
Francesco, I agree with you, but I'd add that at some later date, there may be a good reason to want to get at the private method. A bug appears, for example, and it seems that the private method may be the culprit. But then, as John Roth points out in an earlier comment, maybe it's time to move that method to its own class.
David,
yes, I see your point. I'm not yet 100% sold on it though.
While I agree that there are times when it is very appropriate to refactor a class out of one or more private methods, I'm not sure that the 'there seem to be a bug in the private method' is a good example of it.
Don't get me wrong: I'm not saying it isn't; I just don't have enough experience in that scenario.
I have to wonder if that would be a shortcut (something I tend to be suspicious of).
If, as we were saying, private method X is there just because of refactoring, and we suspect that private method X has some bug under some circumstances that we don't have a unit/acceptance test for, wouldn't it be more appropriate to write the test for public method Y -which uses private method X- to exercise the condition we suspect is bugged ?
See, I am worried that, if I take the 'shortcut' of publicizing method X (or extracting it into its own class so that it is public), I then have to:
~ support a new public API (maybe public only to other developers in the team, maybe even to third party tools developers);
~ write a lot of tests for the newly publicized method X that would not be needed if it remained private (since, while it is private I can assume I know the pre-conditions that apply before method X is called, whereas I cannot make such an assumption if it is public).
Now, I like to write defensive code.. so the idea of assuming the preconditions is not something I always rely on.. but sometimes it feels silly or just wrong to perform the same check over and over in a path of execution. e.g.: public method A checks that the incoming data is valid (and this might be a resource-intensive check); then it calls private method B and private method C, both of which need the data to be valid. Should B and C perform the same check again ? If B and C are private I feel more confident in documenting the assumed pre-condition, but if they were public methods, I wouldn't be so confident.. and I would write the appropriate tests for each method...
Thanks for reading this far :) I'm just very interested in talking about our craft.
yes, I see your point. I'm not yet 100% sold on it though.
While I agree that there are times when it is very appropriate to refactor a class out of one or more private methods, I'm not sure that the 'there seem to be a bug in the private method' is a good example of it.
Don't get me wrong: I'm not saying it isn't; I just don't have enough experience in that scenario.
I have to wonder if that would be a shortcut (something I tend to be suspicious of).
If, as we were saying, private method X is there just because of refactoring, and we suspect that private method X has some bug under some circumstances that we don't have a unit/acceptance test for, wouldn't it be more appropriate to write the test for public method Y -which uses private method X- to exercise the condition we suspect is bugged ?
See, I am worried that, if I take the 'shortcut' of publicizing method X (or extracting it into its own class so that it is public), I then have to:
~ support a new public API (maybe public only to other developers in the team, maybe even to third party tools developers);
~ write a lot of tests for the newly publicized method X that would not be needed if it remained private (since, while it is private I can assume I know the pre-conditions that apply before method X is called, whereas I cannot make such an assumption if it is public).
Now, I like to write defensive code.. so the idea of assuming the preconditions is not something I always rely on.. but sometimes it feels silly or just wrong to perform the same check over and over in a path of execution. e.g.: public method A checks that the incoming data is valid (and this might be a resource-intensive check); then it calls private method B and private method C, both of which need the data to be valid. Should B and C perform the same check again ? If B and C are private I feel more confident in documenting the assumed pre-condition, but if they were public methods, I wouldn't be so confident.. and I would write the appropriate tests for each method...
Thanks for reading this far :) I'm just very interested in talking about our craft.
> ...maybe it's time to move that method to its own class.
I think that's exactly the point. When you have to expose something you don't want to, your design might be too coase grained.
In situations like those, a refactoring session might be overdue.
Unit tests can be black or white box testing but will mostly be none of both (=grey box).
When you follow TDD strictly, the initial tests you do before coding will be black box,
since you don't yet know about the internas of your class yet to be written.
When you add additinal tests, you'll rather go in the white box direction.
I think the more you go in the direction of white box testing, the stronger is the
coupling between your test and the (nonobvious) functionality under test which can slow down
refactoring and further development.
I think that's exactly the point. When you have to expose something you don't want to, your design might be too coase grained.
In situations like those, a refactoring session might be overdue.
Unit tests can be black or white box testing but will mostly be none of both (=grey box).
When you follow TDD strictly, the initial tests you do before coding will be black box,
since you don't yet know about the internas of your class yet to be written.
When you add additinal tests, you'll rather go in the white box direction.
I think the more you go in the direction of white box testing, the stronger is the
coupling between your test and the (nonobvious) functionality under test which can slow down
refactoring and further development.
Add Child Page to ClassesDontEncapsulate