views:

114

answers:

6

Assume i have a private routine that performs some calculation:

private function TCar.Speed: float
{
   Result = m_furlogs * 23;
}

But now i want to begin testing this calculation more thoroughly, so i refactor it out to a separate function:

public function TCar.Speed: float
{
   Result = CalculateSpeed(m_furlogs);
}

private function TCar.CalculateSpeed(single furlogs): float 
{
   Result = furlogs * 23;
}

Now i can perform all kinds of tests on CalculateSpeed:

Check( CalculateSpeed(0)  =  0);
Check( CalculateSpeed(1)  = 23);
Check( CalculateSpeed(2)  = 46);
Check( CalculateSpeed(88) = -1);

Except that i can't perform these tests, because CalculateSpeed is private to TCar. An abstract tennant of unit-testing is that you never test private code - only public interfaces. As a practical matter, *x*Unit is not normally structured to be able to access private methods of the separate class being tested.

The issue is that none of the rest of the class is setup to handle unit-tests. This is the very first routine that will have testing of any kind. And it is very difficult to configure the host class a set of initial conditions that will allow me to test calling CalculateSpeed with every set of inputs that i would like.

The only alternative i can see, is moving this private calculation out into it's own TCarCalculateSpeed class:

public class TCarCalculateSpeed
{  
   public function CalculateSpeed(float furlogs)
   {
      Result = furlogs * 23;
   }
}

A whole class, dedicated to exposing one method, that's supposed to be private, just so i can test it?

Class explosion.

Plus it's private. If i wanted it to be public, i'd rather promote it to public visibility - at least that way i save a separate class being created.

i'd like to add some unit-testing; but it can only be done in small pieces, as code changes. i can't completely redesign functioning 12 year old software, possibly breaking everything, because i wanted to test one internal calculation.

My current, best, thinking is to add a Test method to my Car class, and just call that:

TCar Car = new TCar();
Car.RunTests;

public procedure TCar.RunTests
{
   Check( CalculateSpeed(0)  =  0);
   Check( CalculateSpeed(1)  = 23);
   Check( CalculateSpeed(2)  = 46);
   Check( CalculateSpeed(88) = -1);
}

But now i have to figure out how to have TCar.RunTests get trigged by the external TestRunner, which is only designed to use TestCase classes.

Note: i've tried my damnest to mix syntax from a bunch of languages. In other words: language agnostic.

+1  A: 

Can you create multiple instances of the TCar class with different initial values of m_furlogs? Do you have a getter on speed anywhere? You could validate against that if so.

If it's only internally used, and you really want to test it, you could create a utilities class that holds the logic for the simple calculations. I know it's refactoring, but it's not the class explosion you might be envisioning.

SB
+1 for the 2nd paragraph. Splitting it off is a trade-off that i suppose requires careful thought in each case. The main consensus is to use tricks of the language to bypass object protections and test it in-place. Fortunately my language has such hacks - so i'll do that.
Ian Boyd
+3  A: 

If a method is complicated (and risky) enough to test on its own, it's worth creating a class for it or making it a public member of the existing class - whichever is more suitable, given the characteristics of the existing class.

Jeff Sternal
+1  A: 

Maybe you could create a test class that derives from your class under test?

Here is an example from another SO question

slamidtfyn
The framework i'd use can't do that, but it is a valid idea. +1
Ian Boyd
+1  A: 

In some languages, there is middle ground between private and public. You can expose a logically private method to a unit test without exposing it to the world.

In method documentation, you can document that the method is intended private, but accessible for the purpose of unit-testing.

In Java, for example, you could make the private method package protected, and place the unit test in the same package. In C#, if I recall correctly, you could make it internal. In C++, the unit-test could be a friend.

Andy Thomas-Cramer
+3  A: 

This can't really be quite language-agnostic, as the protection mechanisms and the tactics to bypass them vary quite widely with language.

But most languages do provide bypasses in some form, and as others have noted, there are sometimes protections midway between private and public that make testing easier.

In Java, for example, reflection can be used to private stuff if you really need to, and things can be made protected or package-private so that you don't need reflection.

Generally speaking, if something is complex enough to require testing it should not be buried as a private method or class in something else. It is doing something that warrants its own class.

Rather than worrying about the number of classes, worry about their size and complexity. Many small classes adhering to the Single Responsibility Principle are better than a small number of classes doing complex things internally.

Don Roby
Almost everyone is advocating some sort of language trick to bypass accessibility protections on this method. This is probably what i'd end up using in the language that i'm using. i chose to give the answer to donroby, even though most of you had the same idea.
Ian Boyd
@Ian - Thanks. But remember my primary advice, and that of several others, is that you do in fact pull it out into another class and test it by more normal means. It's nice to know how to bypass protections, but it's usually a good idea to not have to.
Don Roby
+1  A: 

If your language supports compiler defines, you could use these to your advantage.

(Example code in Delphi)

In your unit test project, set a compiler conditional define, either using the project options or in an include file that you include in each and every unit in that project.

{$DEFINE UNIT_TESTS}

In your class code, check the conditional define and switch between public or protected and private accordingly:

{$IFDEF UNIT_TESTS}
  public // or protected 
{$ELSE}
  private
{$ENDIF} 
    function CalculateSpeed: float;

This means your unit tests will have the access they need to your method, while in production code it will still be private.

Marjan Venema
That's a good note.
Ian Boyd