Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is more important, testability of code, or adherence to OOP principles? [closed]

Tags:

oop

tdd

mocking

My teams evolution of TDD includes what appear to be departures from traditional oop.

  1. Moving away from classes that are self sufficient We still encapsulate data where appropriate. But in order to mock any helper classes, we usually create some way to externally set them via constructor or mutator.

  2. We don't use private methods, ever. In order to take advantage of our mocking framework (RhinoMocks) the methods can't be private. This has been the biggest one to "sell" to our traditional devs. And to some degree I see their point. I just value testing more.

What are your thoughts?

like image 884
Jason Slocomb Avatar asked Nov 14 '08 17:11

Jason Slocomb


3 Answers

OOP is just one paradigm among many possible. It's not a goal in itself, it's a means to an end. You don't have to program object-oriented, not if other paradigms are better suited for you.

And countless clever people before you have remarked that unit testing tends to be a lot easier in function-oriented languages than object-oriented ones, simply because the natural unit for a test is a function. It's not a class (which may have private methods and all sorts of weird state), but a function.

Testability on the other hand, does have a value in itself. If your code isn't testable, you can't test it (obviously), and if you can't test it, how can you tell that it works? So if you have to choose one extreme or the other, I'd certainly choose testability.

But an obvious question is, do you really need to test every private method? These are not part of the public contract of a class, and may not be meaningful in isolation. The public methods are important to test because they have a specific purpose, they must fulfill this very specific contract, and leave the object in a consistent state and so on. They're inherently testable, in a way that a private method may not be. (Who cares what a private method does? It's not part of the class contract)

Perhaps a better solution would be to just refactor some of the otherwise private stuff out into separate classes. Perhaps the need for testing private methods isn't as big as you've been thinking.

On a different note, other mocking frameworks do allow you to mock private stuff too.

Edit: After thinking about it a bit further, I'd like to stress that just making private members public is probably a horrible idea. The reason we have private members in the first place is this: The class invariant must be maintained at all times. It must be impossible for external code to bring your class into an invalid state. That's not just OOP, it's also common sense. Private methods are simply to allow you finer granularity internally in the class, factoring some tasks out across multiple methods and such, but they generally do not preserve the class invariant. They do half the job, and then rely on some other private method being called afterwards to do the other half. And that's safe because they're not generally accessible. Only other class methods can call them, so as long as they preserve the invariant, all is well.

So while yes, you make the private methods testable by turning them public, you also introduce a source of bugs which can not be caught easily by unit tests. You make it possible to use the class "wrong". A well designed class always maintains its invariant, no matter how it's used by external code. Once you make everything public, that is no longer possible. External code can call internal helper functions which may not be used in that context, and which will throw the class into an invalid state.

And unit-tests can't really guarantee that this doesn't happen. So I'd say you risk introducing a much bigger source of errors than you might have expected.

Of course, given this above definition of private members (those that don't preserve the class invariant), it might be possible to safely turn a lot of other methods public, because they do preserve the invariant, and so there's no need to hide them from external code. So that might make lessen your problem, by giving you fewer private methods, but without allowing external code to break your class, as would be possible if everything was public.

like image 113
jalf Avatar answered Nov 23 '22 23:11

jalf


i am not familiar with rhinomocks, and in fact have never used or needed a mocking tool, so i may be way off base here, but:

  • there should be no conflict between OO principles and TDD, because
  • private methods do not need to be unit tested, only public ones
like image 33
Steven A. Lowe Avatar answered Nov 23 '22 23:11

Steven A. Lowe


What you are experiencing is that tests exert forces on the design. That's actually the reason why TDD is mainly a design strategy - writing the tests forces a better decoupled design, if you pay attention and know how to read the signs.

Injecting "helper objects" into classes is in fact a good thing. You shouldn't think of them as helper objects, though. They are regular objects, hopefully at a different level of abstraction. They are basically Strategies, that configure how the details of the higher level algorithms get filled in. (I typically inject them using a constructor, and also provide another constructor that automatically uses the default production implementation, if there is one.) Beware though - mocking can also be overdone, in my experience. Take a look at http://martinfowler.com/articles/mocksArentStubs.html for some interesting thoughts on the topic.

Regarding private methods, I don't fully understand what this has to do with your mocking framework - typically, you should mock interfaces, which only have public methods, anyway, which are part of the public contract.

Anyway, complex private methods are a code smell, in my opinion - it's a sign that the class probably is taking on too much responsibility, violating the Single Responsibility Principle. Think about on what kind of class it would actually not be a violation of encapsulation to make it public. Moving the method to that class (possibly creating it on the way) is likely going to improve your design in terms of coupling and cohesion.

like image 22
Ilja Preuß Avatar answered Nov 24 '22 01:11

Ilja Preuß