views:

450

answers:

6

Many times I find myself torn between making a method private to prevent someone from calling it in a context that doesn't make sense (or would screw up the internal state of the object involved), or making the method public (or typically internal) in order to expose it to the unit test assembly. I was just wondering what the Stack Overflow community thought of this dilemma?

So I guess the question truly is, is it better to focus on testability or on maintaining proper encapsulation?

Lately I've been leaning towards testability, as most of the code is only going to be leveraged by a small group of developers, but I thought I would see what everyone else thought?

+19  A: 

Its NOT ok to change method visibility on methods that the customers or users can see. Doing this is ugly, a hack, exposes methods that any dumb user could try to use and explode your app... its a liability you do not need.

You are using C# yes? Check out the internals visible to attribute class. You can declare your testable methods as internal, and allow your unit testing assembly access to your internals.

Charles
As you can tell from my question, I'm aware I can make a method internal and visible only to other classes in the assembly (or friend assemblies). I find that sometimes even internal methods make an object model seem less concise or well thought out to those developing in the same assembly, hence the reason for the question :)
Eric
+2  A: 

This is a common thought.

It's generally best to test the private methods by testing the public methods that call them (so you don't explicitly test the private methods). However, I understand that there are times when you really do want to test those private methods.

The answers to this question (Java) and this question (.NET) should be helpful.

To answer the question: no, you shouldn't change method visibility for the sake of testing. You generally shouldn't be testing private methods, and when you do, there are better ways to do it.

David Johnstone
+8  A: 

It depends on whether the method is part of a public API or not. If a method does not belong to part of a public API, but is called publicly from other types within the same assembly, use internal, friend your unit test assembly, and unit test it.

However, if the method is not/should not be part of a public API, and it is not called by other types internal to the assembly, DO NOT test it directly. It should be protected or private, and it should only be tested indirectly by unit testing your public API. If you write unit tests for non-public (or what should be non-public) members of your types, you are binding test code to internal implementation details.

Thats a bad kind of coupling, increases the amount of unit tests you need, increases workload both in the short term (more unit tests) as well as in the long term (more test maintenance and modification in response to refactoring internal implementation details). Another problem with testing non-public members is that you test code that may not actually be needed or used. A GREAT way to find dead code is when it is not covered by any of your unit tests when your public API is covered 100%. Removing dead code is a great way to keep your code base lean and mean, and is impossible if you are not careful about what you put into your public API, and what parts of your code you unit test.

EDIT: As a quick additional note...with a properly designed public API, you can very effectively use a tool like Microsoft PEX to automatically generate full-coverage unit tests that test every execution path of your code. Combined with a few manually written tests that cover critical behavior, anything not covered can be considered dead code and removed, and you can greatly shortcut your unit testing process.

jrista
You hit on the crux of my problem - making methods internal still exposes too much, as these are methods other classes in the assembly shouldn't be calling. I very much agree with your thoughts on the fact that testing what should be non-public methods binds the test code to the implementation too tightly.
Eric
@Eric: then you document and enforce that those methods should not be called. The new Layer Diagrams feature of VS2010 may help there, as one can create restrictions on how one layer can interact with another - those restrictions can be enforced during builds.
John Saunders
Glad to be of service. :) I added a update regarding PEX that you might be interested in.
jrista
A: 

I normally declare those private methods as protected, and then create a testable subclass in the unit test solution.

class A {
 protected void Foo(...) { ... }
}

class TestableA : A {
 public void PublicFoo(...) { base.Foo(...); }
}
m0sa
This creates and undesired coupling between your unit tests and your private implementation details. See my answer for a more complete answer...but I would GREATLY discourage this as a general practice.
jrista
A: 

In general I agree with @jrista. But, as usual, it depends.

When trying to work with legacy code, the key is to get it under test. After that, you can add tests for new features and existing bugs, refactor to improve design, etc. This is risky without tests. Legacy code tends to be rife with dependencies, and is often extremely difficult to get under test.

In Working Effectively with Legacy Code, Michael Feathers suggests multiple techniques for getting code under test. Many of these techniques involve breaking encapsulation or complicating the design, and the author is up front about this. Once tests are in place, the code can be improved safely.

So for legacy code, do what you have to do.

TrueWill
A: 

In .NET you should use Accessors for unit testing, even rather than the InternalsVisibleTo attribute. Accessors allow you to get access to any method in the class even if it is private. They even let you test abstract classes using an empty mock derived object (see the "PrivateObject" class).

Basically in your test project you use the accessor class rather than the actual class with the methods you want to test. The accessor class is the same as the "real" class, except everything is public to your test project. Visual studio can generate accessors for you.

NEVER make a type more visible to facilitate unit testing.

IMO is it WRONG to say that you should not unit test private methods. Unit tests are of exceptional value for regression testing and there is no reason why private methods should not be regression tested with granular unit tests.

MrLane