2010-09-29 17 views
5

Supongamos que tengo una rutina privada que realiza alguna cálculo:¿Cómo probar la unidad de código privado sin refactorizar para separar la clase?

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

Pero ahora quiero empezar a probar este cálculo más a fondo, así que refactorizar a una función separada:

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

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

Ahora i puede realizar todo tipo de pruebas en CalculateSpeed:

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

Excepto que no puedo realizar estas pruebas, porque CalculateSpeed es privado a TCar. Un tenant abstracto de pruebas unitarias es que nunca se prueba el código privado, solo las interfaces públicas. Como cuestión práctica, la unidad * x * normalmente no está estructurada para poder acceder a métodos privados de la clase separada que se está probando.

El problema es que ninguna parte del resto de la clase está configurada para manejar pruebas unitarias. Esta es la primera rutina que tendrá pruebas de cualquier tipo. Y es muy difícil configurar la clase de host un conjunto de condiciones iniciales que me permitirán probar llamar a CalculateSpeed con cada conjunto de entradas que me gustaría.

La única alternativa que puedo ver, se está moviendo este cálculo privada fuera en su propia clase TCarCalculateSpeed:

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

toda una clase, dedicada a la exposición de un método, que se supone que es privado, sólo para poder ¿Pruébalo?

Explosión de clase.

Además, es privado. Si quisiera que fuera público, preferiría promocionarlo a público visibilidad - al menos de esa manera guardo una clase separada que se está creando.

me gustaría agregar algunas pruebas de unidad; pero solo se puede hacer en pequeños fragmentos, a medida que cambia el código. No puedo rediseñar por completo el software de 12 años de funcionamiento, posiblemente rompiendo todo, porque quería probar un cálculo interno.

Mi actual, el mejor, el pensamiento es añadir un método Test a mi clase Car, y acaba de llamar que:

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); 
} 

Pero ahora tengo que encontrar la manera de tener TCar.RunTests conseguir trigged por el TestRunner externa , que solo está diseñado para usar las clases TestCase.

Nota: He intentado con todo mi corazón mezclar la sintaxis de varios idiomas. En otras palabras: idioma agnóstico.

+0

No sé cómo hacerlo de una manera independiente del idioma, pero en VS/.NET, creará una clase de Accessor, que sombrea tu clase real y hace todo público para que puedas probarlo. – CaffGeek

+0

Esa podría ser la respuesta que tengo disponible. Tendría que hacer el método 'protected', en lugar de' private', pero luego puedo acceder a los miembros protegidos desde mi archivo de código de prueba de unidad. –

Respuesta

3

Esto realmente no puede ser bastante independiente del idioma, ya que los mecanismos de protección y las tácticas para eludirlos varían ampliamente con el idioma.

Pero la mayoría de los idiomas proporcionan desvíos de alguna forma, y ​​como otros han notado, a veces hay protecciones a medio camino entre lo privado y lo público que facilitan las pruebas.

En Java, por ejemplo, la reflexión se puede utilizar para cosas privadas si realmente se necesita, y las cosas se pueden proteger o se pueden hacer paquetes privados para que no necesite reflexión.

En general, si algo es lo suficientemente complejo como para requerir pruebas, no debe ser enterrado como un método o clase privada en otra cosa. Está haciendo algo que merece su propia clase.

En lugar de preocuparse por el número de clases, preocúpese por su tamaño y complejidad. Muchas clases pequeñas que se adhieren al Single Responsibility Principle son mejores que un pequeño número de clases que hacen cosas complejas internamente.

+0

Casi todo el mundo aboga por algún tipo de truco de lenguaje para eludir las protecciones de accesibilidad en este método. Esto es probablemente lo que terminaría usando en el lenguaje que estoy usando. Elegí dar la respuesta a donroby, aunque la mayoría de ustedes tenía la misma idea. –

+1

@Ian - Gracias. Pero recuerda que mi principal consejo, y el de muchos otros, es que de hecho lo extiendes en otra clase y lo pruebas por medios más normales. Es bueno saber cómo evitar las protecciones, pero generalmente es una buena idea no tener que hacerlo. –

1

¿Puede crear varias instancias de la clase TCar con diferentes valores iniciales de m_furlogs? ¿Tienes un getter de velocidad en cualquier lugar? Podrías validar contra eso si es así.

Si solo se usa internamente, y realmente desea probarlo, puede crear una clase de utilidades que contenga la lógica para los cálculos simples. Sé que se está refabricando, pero no es la explosión de clase que podrías estar imaginando.

+0

+1 para el segundo párrafo.Dividirlo es una disyuntiva que, supongo, requiere una reflexión cuidadosa en cada caso. El consenso principal es usar trucos del lenguaje para eludir las protecciones de objetos y probarlas en el lugar. Afortunadamente mi lenguaje tiene tales hacks, así que lo haré. –

3

Si un método es complicado (y arriesgado) suficiente para probarlo solo, vale la pena crear una clase o convertirlo en un miembro público de la clase existente, lo que sea más adecuado, dadas las características de la clase existente .

1

¿Tal vez podría crear una clase de prueba que se deriva de su clase bajo prueba?

Aquí es un ejemplo de otro SO question

+0

El marco que utilizaría no puede hacer eso, pero es una idea válida. +1 –

1

En algunos idiomas, no hay término medio entre privado y público. Puede exponer un método lógicamente privado a una prueba de unidad sin exponerlo al mundo.

En la documentación de métodos, puede documentar que el método está destinado a ser privado, pero accesible a efectos de pruebas unitarias.

En Java, por ejemplo, puede hacer el método privado package protected, y colocar la prueba de unidad en el mismo paquete. En C#, si recuerdo correctamente, podrías hacerlo interno. En C++, la prueba de unidad podría ser un amigo.

1

Si su lenguaje admite definiciones de compilador, puede usarlas para su ventaja.

(Código de ejemplo en Delphi)

En su proyecto de prueba de unidad, establezca un compilador condicional definir, ya sea usando las opciones del proyecto o en un archivo de inclusión que incluya en todos y cada unidad en ese proyecto.

{$DEFINE UNIT_TESTS} 

En su código de clase, comprobar definen el condicional y cambiar entre público o protegido y privada en consecuencia:

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

Esto significa que las pruebas unitarias tendrán el acceso que necesitan a su método, mientras que en código de producción seguirá siendo privado.

+0

Esa es una buena nota. –

Cuestiones relacionadas