I used to think that a constructor should do everything necessary to make sure the object is going to be usable. "Fail fast," as they say.
I don't think that way any more.
My tale of woe began when I wanted to conume a class in a library that was apparently written without unit testing in mind. Let's say the class was called LibraryClass. I wanted to write unit tests for my consuming class that verified I was calling LibraryClass as expected. LibraryClass required all manner of infrastructure that I did not want to set up for my unit tests. A perfect situation for mocking, right?
Not so fast. LibraryClass did not have an interface and I could not add one because my consuming code had to be compatible with the already-published version of LibraryClass. What would form the basis of my mock?
How about a partial mock? As I explained here, a partial mock calls the mocked class' default constructor and then lets you set up further aspects of the mock from there, overriding the real object's behavior. Perfect!
Then came the gotcha.
LibraryClass' constructor made calls to the very infrastructure I was trying to avoid. In fact, the constructor would fail if I did not have a certain database, with a certain record in it, and a certain WCF service running. D'oh!
If the constructor had limited its activities to initializing readonly variables and the like, I would have been all set. Lazy-initializing, dependency injection, or other techniques could have taken care of the rest. Not to mention partial mocks.
Bare-bones constructors also have the advantage of making their objects cheap to construct over and over again -- something else you want in unit tests, where speed is important. (You should have many, many unit tests, and in a test-driven-development environment, you run the tests a lot.)
So let's be sure to put as little code in our constructors as possible.