2008-12-22 9 views
15

Hay un aspecto de TDD que nunca entendí del todo.TDD: ¿Qué métodos expone para las pruebas unitarias?

Supongamos que alguien le pide que implemente un objeto Stack simple. Si ha hecho su diseño correctamente, obtendrá una API mínima y limpia. Supongamos: push(), pop() y isEmpty(). Cualquier cosa más que eso está exagerando la demanda y permitiendo que el usuario tenga demasiado espacio para meterse con su código.

Ahora supongamos que quiere probar su código unitariamente. ¿Cómo hace esto si todos sus métodos públicos son solo los tres que se muestran arriba? Esos métodos tomarán sus pruebas solo hasta ahora.

Por lo tanto, puede agregar métodos privados, que no lo ayudan en absoluto, ya que no son visibles para su caso de prueba de la unidad. O haces públicos esos métodos y ahí va tu API minimalista con la que trabajaste tan duro. Ahora el usuario va a meterse con su pila y los errores seguramente llegarán.

¿Cómo maneja este dilema de abrir los métodos públicos para las pruebas frente a una API limpia y simple?

Editar: acaba de apuntar en la dirección correcta, sería bueno para obtener punteros técnicos (como "utilizar este truco para exponer los métodos privados", etc ...) pero yo soy mucho más intrested en más respuestas genéricas en cuanto a cuál de los dos conceptos es más importante, y cómo abordar este tema.

+0

Aquí hay otra pregunta sobre el mismo tema: [¿Cómo pruebas los métodos privados de la unidad?] (Http://stackoverflow.com/questions/250692/how-do-you-unit-test-private-methods) –

Respuesta

14
  1. probar las funciones; esto generalmente significa probar la interfaz pública, ¿no deberían todas las características ser accesibles a través de la interfaz pública? si no lo son, ¡entonces no son características! Puede haber excepciones a esto, pero no puedo pensar en ninguna.

  2. prueba la interfaz pública; cualquier método que no se llame directa o indirectamente desde la interfaz pública es no es necesario. No solo no necesitan ser probados, no necesitan existir en absoluto.

+1

Recomiendo esta aclaración: si comienza a sentir la necesidad de exponer algo privado para probar, NO lo haga, sino que lo vea como una señal de que su implementación es demasiado compleja y que debe reconsiderar la granularidad de su implementación y su pruebas. –

2

A veces hago métodos que de otro modo serían privados en los métodos de nivel de paquete (Java) o interno (.NET), generalmente con un comentario o una anotación/atributo para explicar por qué.

La mayoría de las veces, evito hacer esto. ¿Qué no le dejaría la API pública probar en su caso de pila? Si el usuario puede ver el error y solo está utilizando la API pública, ¿qué le impide hacer las mismas llamadas?

Las veces que expongo métodos que de otro modo serían privados es cuando hace más fácil probar una parte de un complicado conjunto de pasos en forma aislada: si una sola llamada de método es muy "gruesa", puede ser muy útil probar cada paso de forma aislada, aunque los pasos no deberían ser visibles para un usuario normal.

6

Usted debe echar un vistazo a esa pregunta: do you test private method?.

Para no romper la encapsulación, me parece que el método privado es enorme o complejo o lo suficientemente importante como para requerir sus propias pruebas, simplemente lo pongo en otra clase y lo hago público allí (Objeto de método). Entonces puedo probar fácilmente el método previamente privado pero ahora público que ahora vive en su propia clase.

0

Puede realizar pruebas privadas con Reflection, pero será un dolor de cabeza más adelante. Creo que debes poner tu prueba en el mismo conjunto (o paquete) e intentar usar Internal. De esta manera, tienes algo de protección y puedes probar tus cosas que quieres probar.

2

TDD derecho tiene que ver con el comportamiento de las pruebas que podrían ser probada por la interfaz pública ... Si hay algún métodos privados entonces esos métodos deben ser probados indirectamente por cualquiera interfaz pública ...

2

Con TDD, todos de su código debe ser accesible desde su interfaz pública:

  • Primero escribe las pruebas para sus características.
  • Luego, escribe la cantidad mínima de código para pasar las pruebas. Esta es la indicación de que sus funciones están implementadas.

Si algún código no está cubierto, significa que este código es inútil (quítelo y ejecútelo de nuevo) o sus pruebas están incompletas (algunas características se implementaron pero no se identificaron explícitamente mediante una prueba).

4

Usando su ejemplo de pila, realmente no debería necesitar exponer el funcionamiento interno para probarlo en la unidad. Reiterando lo que otros han dicho, debes sentirte libre de tener tantos métodos privados como necesites, pero solo prueba a través de tu API pública.

[Test] 
public void new_instance_should_be_empty() 
{ 
    var stack = new Stack(); 

    Assert.That(stack.IsEmpty(), Is.True); 
} 

[Test] 
public void single_push_makes_IsEmpty_false() 
{ 
    var stack = new Stack(); 

    stack.Push("first"); 

    Assert.That(stack.IsEmpty(), Is.False); 
} 

Usando una combinación de empujar y hacer estallar puede simular todo el comportamiento de la clase industrial, porque esa es la única manera que un usuario tiene que interactuar con él. No necesitas ningún truco porque solo debes probar lo que otros pueden usar.

[Test] 
public void three_pushes_and_three_pops_results_in_empty_stack() 
{ 
    var stack = new Stack(); 

    stack.Push("one"); 
    stack.Push("two"); 
    stack.Push("three"); 
    stack.Pop(); 
    stack.Pop(); 
    stack.Pop(); 

    Assert.That(stack.IsEmpty(), Is.True); 
} 
+0

Prefiero esto más que la respuesta aceptada. ¡Este ejemplo de código muestra de manera excelente cómo tus pruebas deberían enfocarse en la API y los resultados, y no en la implementación! – kai

-1

Al codificar utilizando TDD, creo la API de interfaz pública para el objeto. Eso significaría en su ejemplo que mi interfaz que implementa la clase solo tendría push(), pop() y isEmpty().

Sin embargo probar estos métodos llamando ellos no son pruebas de unidad en sí mismos (más sobre esto al final de este post), ya que prueba más probable es que la cooperación de múltiples métodos de interiores que juntas producen el resultado deseado y de eso se trata su pregunta: ¿Deberían esos métodos ser privados?

Mi respuesta es no, use protected (o el equivalente en el idioma de su elección) para aquellos en lugar de private, lo que significa que si su proyecto y POM de prueba están estructurados de manera similar, la clase de suite de prueba puede ver dentro del clase real ya que están prácticamente en la misma carpeta. Estas pueden considerarse pruebas unitarias: estás probando bloques funcionales de la clase en sí, no su cooperación.

En cuanto a las pruebas de los métodos de la API de interfaz/individuales Es por supuesto importante hacer eso también y yo no discuto eso, los que sin embargo caen en algún lugar entre la línea nebuloso de unit testing y acceptance testing.

En mi opinión, lo más importante para recordar aquí es que las pruebas unitarias indican si un método funciona mal, las pruebas de aceptación indican si la cooperación de métodos múltiples '/ classes' se comporta mal y las pruebas de integración la operación se comporta mal

+0

¿qué definición de "unidad" estás usando que dice que un método privado califica? la mayoría de las definiciones que veo hablan de "un método público" o "un conjunto discreto de métodos en una clase pública", o más abstracto "una sola pieza de código que realiza una tarea real de tal manera que puede afirmarse en contra de algún resultado concreto (cambio de estado, valores reales de retorno, llamadas externas realizadas, etc.) – kai

0

Si su método privado no es probado indirectamente por los métodos de prueba API públicos y debe ser probado, entonces delegaría su clase principal a otra clase secundaria.

public Stack { 
    public ... push(...) {...} 
    public ... pop(...) {...} 
    public ... isEmpty(...) {...} 

    // secondary class 
    private StackSupport stackSupport; 
    public StackSupport getStackSupport() {...} 
    public void setStackSupport(StackSupport stackSupport) {...} 
} 

public StackSupport { 
    public ...yourOldPrivateMethodToTest(...) {...} 
} 

Entonces su método privado es un método público en otra clase. Y puedes probar ese método público en las pruebas de otra clase. :-)

Cuestiones relacionadas