Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit-test private code without refactoring to separate class?

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.

like image 358
Ian Boyd Avatar asked Sep 29 '10 19:09

Ian Boyd


2 Answers

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.

like image 66
Don Roby Avatar answered Sep 28 '22 10:09

Don Roby


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.

like image 23
Jeff Sternal Avatar answered Sep 28 '22 09:09

Jeff Sternal