4

If I have a type with a big-old (lots of params) constructor, is it a valid approach to implement a protected parameterless constructor simply for the purposes of creating a derived "Fake" type to use for stubbing in unit tests?

The alternative is to extract an interface, but this is not always desireable in a codebase you do not have full control over...

Ben Aston
  • 53,718
  • 65
  • 205
  • 331

4 Answers4

2

Since you essentially have to treat protected the same as public, the answer would be no from a strictly object-oriented point of view.

You could add a private parameterless constructor though and invoke it through reflection, if that's not too much hassle.

TToni
  • 9,145
  • 1
  • 28
  • 42
2

It's not ideal, but it is valid.

To quote a couple of people who know more about this than me, in The Art of Unit Testing, Roy Osherove talks about unit tests being like a user of the code, and as such providing access specifically for them is not necessarily a bad thing.

And in Working Effectively with Legacy Code, Michael Feathers discusses several such techniques, pointing out that making things protected for testing can be better than making them public, although it's not necessarily the best way to do things. (In fact I'd recommend you read that book if you are working with legacy code, as it sounds like you are).

I think it depends what sort of code it is - if it's a public API where people are likely to take protected access to mean it's designed to be overridden, it's probably a bad idea, but on a typical business app where it's pretty obviously not meant to be overridden, I don't think it's a problem. I certainly do it sometimes in this situation.

Grant Crofton
  • 8,895
  • 5
  • 28
  • 38
  • Can a framework like RhinoMocks assist me when dealing with ctors with large numbers of parameters? – Ben Aston Oct 20 '10 at 14:05
  • Well, it can help you make some of the parameters to pass in, if they're mockable (which many things are with Rhino). If they're not, one thing you might be able to do is 'extract interface' on some of the parameter types, use the interface instead of the concrete type, and use Rhino to mock those interfaces. But AFAIK, it can't automatically mock them all for you without you telling it to. – Grant Crofton Oct 20 '10 at 14:18
1

Could you not create a class which extends the class you want to test? That lets you use your own constructor without having to create an interface.

Modifying your code or API to facilitate unit testing is generally undesirable (see the "Should I unit test private methods" debate), so actually using a new class rather than modifying your existing one might be the way forward.

Andy
  • 2,764
  • 6
  • 24
  • 33
  • Implementing a derived type only moves the problem of having to stub, or instantiate the arguments for the base ctor though, no? – Ben Aston Oct 20 '10 at 13:35
  • My point was aimed more at addressing the difficulty of making changes within a codebase you don't always have control over - moving it out mitigates that somewhat. As you say, hardly a perfect solution. – Andy Oct 20 '10 at 13:51
0

There is the argument that non-sealed types should always declare a protected parameterless constructor (where your others have parameters).

This allows flexibility for subclasses to instantiate the class in their own way, not forced to provide parameters to the base constructor.

Mocking frameworks are just such an example of a subclass that wants the freedom to be able to instantiate the class in its own way; the mock object doesn't care for setting property defaults or dependencies since it will mock the return values anyway.

One day, your own classes or library customers may have wildly different ideas about setting up the class, and you don't want a type that you cannot evolve months down the line because you're forced to push something that's no longer necessary into the base ctor.

There are rare cases when you absolutely need to force some initialization value, but it is rare - note that structs should not have parameterless ctors.

Luke Puplett
  • 42,091
  • 47
  • 181
  • 266