views:

593

answers:

15

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?

+14  A: 

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
Steven A. Lowe
Agreed. Sounds like more of a problem with the use of the mocking tool. Testability is _increased_ with encapsulation and good OO principles.
Terry Lorber
That's a too simple way of looking at it. OOP is about encapsulating objects, so they work as a black box to external code. Unit tests are about treating everything as a white box, taking every little component out of its context, and testing it in isolation. That's a conflict.
jalf
@jalf: test-driven-design is not white-box testing
Steven A. Lowe
That's true. Perhaps I was too quick on the trigger there. :)My point was simply that OOP and testability (not necessarily TDD) is not necessarily the perfect match. Plenty of conflicts can occur between the two, and OOP is not "more" testable as Terry said (more testable than what, anyway?). :)
jalf
@jalf Encapsulation is about putting data and behavior on the same object - don't confuse it with information hiding. White box testing is about the developer knowing about the algorithm used in the code. It's mostly orthogonal to both encapsulation and information hiding.
Ilja Preuß
@Steven TDD is neither white box nor black box testing - as most of the code you are testing might not be even written yet, you also can't know about it, abviously. On the other hand, you do have full knowledge about the code that already is written, and make full use of that knowledge.
Ilja Preuß
@[Ilja Preuß]: cf http://en.wikipedia.org/wiki/Information_hiding "The term encapsulation is often used interchangeably with information hiding. Not all agree on the distinctions between the two though; one may think of information hiding as being the principle and encapsulation being the technique"
Steven A. Lowe
@[Ilja Preuß]: cf http://en.wikipedia.org/wiki/Test-driven_development "In test-driven development, each new feature begins with writing a test." Since only the features are tested, and the tests are written before the code, this sure sounds like a white-box testing approach to me...
Steven A. Lowe
A: 

As a regular user of Intellisense, the requirement that all methods be public would drive me insane. I would adhere to OOP on that basis alone, personally.

MusiGenesis
+1  A: 

Dependency Injection & Inversion of Control are fairly good ways to get the best of both worlds. Good design should not limit your ability to use the code in different ways (test), it should improve it.

We're re-writing a whole bunch of singletons to use DI/IOC right now so that we can test them (coming soon to a cable box near you).

Bill K
+1  A: 

You can also investigate dependency injection as a way to externalize some of the helper classes

Tai Squared
+2  A: 

If there is a framework which restricts me from using something like private methods, I drop it. Period. That's one of those fundamental things in most languages that's needed.

Kevin
+18  A: 

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.

jalf
The first two paragraphs in your answer seem to contradict the later stuff...
Tim
Yeah, I wrote the second part separately after reconsidering a bit. I don't think there are any contradictions though. The first part is really a choice between extremes (testability *or* OOP?), the later part points out the problems with completely disregarding OOP practices. Compromise is key. :)
jalf
+1  A: 

I use Rhino Mocks and I use lots of private methods. I usually write my classes to depend on interfaces rather than other classes, because that makes mocking easier.

Don Kirkby
+1  A: 

Regarding the classes scope, you could use internals to simplify the framework (for the end-users) and specify in the framework project AssemblyInfo.cs

[assembly: InternalsVisibleTo("MyAssembly.Tests")]
labilbe
+4  A: 

Good OO design is more than encapsulation, more than having private methods.

One of the major design smells is coupling between different parts of the system. Before you were injecting helper classes into your objects to test them, how were your objects getting access to the helpers?

My guess is that by switching to dependency injection you've lowered the coupling in your system.

There's also the Open/Closed principle. By injecting helper classes you allow polymorphic substitution of behavior.

And if there's some concern about having non-private methods visible on a class, use the class through its interface, which is a good idea anyway, right?

Jeffrey Fredrick
+4  A: 

Maintainability is more important.

Don't miss the point that both, unit testing and OOP have that goal in common.

Null303
+1  A: 

I am not familiar with any conflict between them all. High cohesion and low coupling are the core of OO and that happens to be the core of testing as well.

Todd Hoff
+2  A: 

OOP is a tool, not a goal

Javier
And testability is a goal, but only for testing, which is a tool.
Null303
OOP is not a tool. It is a design midnset.
Tim
@tim: i agree, it's a design mindset, and not a language feature; but as most programming concepts, it is a tool to an end: better programs
Javier
@Javier If by better programs you mean better maintainable code, I agree. The user of your program probably couldn't care less about whether you used OOP to implement it or not, does he?
Ilja Preuß
right, that's why it's just a tool, among many others
Javier
A: 

Why not just use a macro instead of the private keyword. WHen you compile with "testmode" on, those methods are public. Otherwise they are private. With a macro, you'll still get compiler warnings when you use your private methods publicly, once you compile not in testing mode. It is interesting to note that having your private methods fail their unit tests does not imply a bug in your program, though it is likely equivalent to having a function, "CauseBSOD" which is never called. It's a heavily broken function (assuming causing BSOD's is not intended), but it isn't a bug as far as users are concerned.

Brian
+8  A: 

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.

Ilja Preuß
A: 

Why can't you use friend classes that are only test classes? Alternatively your test classes derive from the parent classes that have private members.

Making everything public to cater to bad testing tools is a mistake in my opinion. As others have said - this shouldn't have to be a choice. Design good software. Test well and use good tools. If you have to break well-known best practices for some test tool then it is time to rethink your test tool...

Tim