2009-11-13 20 views
18

Estoy tratando de mejorar el número y la calidad de las pruebas en mis proyectos de Python. Una de las dificultades que he encontrado a medida que aumentan las pruebas es saber qué hace cada prueba y cómo se supone que ayuda a detectar problemas. Sé que parte del seguimiento de las pruebas es una mejor prueba de unidad de nombres (que se ha tratado elsewhere), pero también me interesa saber cómo la documentación y las pruebas unitarias van de la mano.¿Cómo deberían documentarse las pruebas unitarias?

¿Cómo se pueden documentar las pruebas unitarias para mejorar su utilidad cuando esas pruebas fallan en el futuro? Específicamente, ¿qué hace una buena prueba de unidad docstring?

Agradecería las respuestas descriptivas y ejemplos de pruebas unitarias con documentación excelente. Aunque estoy trabajando exclusivamente con Python, estoy abierto a las prácticas de otros idiomas.

Respuesta

13

documento más que en mis pruebas de unidad con el nombre del método exclusivamente:

testInitializeSetsUpChessBoardCorrectly() 
testSuccessfulPromotionAddsCorrectPiece() 

Para casi el 100% de mis casos de prueba, lo que explica claramente lo que la prueba de la unidad está validando y eso es todo lo que uso. Sin embargo, en algunos de los casos de prueba más complicados, agregaré algunos comentarios a lo largo del método para explicar lo que están haciendo varias líneas.

He visto una herramienta antes (creo que era para Ruby) que generaría archivos de documentación al analizar los nombres de todos los casos de prueba en un proyecto, pero no recuerdo el nombre. Si tenías casos de prueba para un juego de ajedrez de clase reina:

testCanMoveStraightUpWhenNotBlocked() 
testCanMoveStraightLeftWhenNotBlocked() 

la herramienta podría generar un documento HTML con contenido algo como esto:

Queen requirements: 
- can move straight up when not blocked. 
- can move straight left when not blocked. 
+0

¿Qué pasa con los nombres de sus funciones?Supongo que nombre sus pruebas "testFunctionName" y eso está bien, ¿pero realmente tiene una función llamada InitializeSetsUpChessBoardCorrectly? Creo que "setUpChessboard" estaría bien. –

+10

No, el nombre del método explica exactamente lo que está probando: el caso de prueba verifica que initalize() configura correctamente el tablero de ajedrez. Boom, documentación automática. –

+0

Jaja, sí, la "prueba" al principio es solo de los viejos tiempos de JUnit, que mi cerebro todavía está atrapado. Podría simplemente nombrarlo initalizeSetsUpChessBoardCorrectly() y usar una anotación @Test. –

4

El nombre del método de ensayo debe describir exactamente lo que eres pruebas. La documentación debe indicar qué hace que la prueba falle.

1

Debe usar una combinación de nombres de métodos descriptivos y comentarios en la cadena de documentación. Una buena forma de hacerlo es incluir un procedimiento básico y pasos de verificación en la cadena de documentación. Luego, si ejecuta estas pruebas desde algún tipo de marco de prueba que automatice la ejecución de las pruebas y la recopilación de resultados, puede hacer que el marco registre el contenido de la cadena de documentación para cada método de prueba junto con su stdout + stderr.

Aquí hay un ejemplo básico:

class SimpelTestCase(unittest.TestCase): 
    def testSomething(self): 
     """ Procedure: 
      1. Print something 
      2. Print something else 
      --------- 
      Verification: 
      3. Verify no errors occurred 
     """ 
     print "something" 
     print "something else" 

Tener el procedimiento con la prueba hace que sea mucho más fácil de averiguar lo que la prueba está haciendo. Y si incluye la docstring con el resultado de la prueba, resulta mucho más fácil descifrar lo que salió mal al revisar los resultados más adelante. El lugar anterior en el que trabajé hizo algo como esto y funcionó muy bien cuando ocurrieron fallas. Ejecutamos las pruebas unitarias en cada checkin automáticamente, usando CruiseControl.

+0

http://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/ –

8

Quizás el problema no radique en la mejor manera de escribir docstrings de prueba, ¿pero cómo escribir las pruebas por sí mismos? Refactorizar las pruebas de forma tal que sean auto documentadas puede ser de gran ayuda, y su docstring no se quedará obsoleto cuando cambie el código.

Hay algunas cosas que usted puede hacer para hacer las pruebas más claras:

  • claras & nombres de los métodos de prueba descriptiva (ya mencionado)
  • cuerpo de prueba debe ser clara y concisa (autodocumentado)
  • resumen la configuración/desmontaje complicado etc. en los métodos
  • más?

Por ejemplo, si tiene una prueba como esta:

def test_widget_run_returns_0(): 
    widget = Widget(param1, param2, "another param") 
    widget.set_option(true) 
    widget.set_temp_dir("/tmp/widget_tmp") 
    widget.destination_ip = "10.10.10.99" 

    return_value = widget.run() 

    assert return_value == 0 
    assert widget.response == "My expected response" 
    assert widget.errors == None 

Es posible reemplazar las instrucciones de configuración con una llamada al método:

def test_widget_run_returns_0(): 
    widget = create_basic_widget() 
    return_value = widget.run() 
    assert return_value == 0 
    assert_basic_widget(widget) 

def create_basic_widget(): 
    widget = Widget(param1, param2, "another param") 
    widget.set_option(true) 
    widget.set_temp_dir("/tmp/widget_tmp") 
    widget.destination_ip = "10.10.10.99" 
    return widget 

def assert_basic_widget(): 
    assert widget.response == "My expected response" 
    assert widget.errors == None 

nota de que su método de ensayo se compone ahora de una serie de llamadas a métodos con nombres reveladores de intenciones, una especie de DSL específico para sus pruebas. ¿Una prueba como esa todavía necesita documentación?

Otra cosa a tener en cuenta es que su método de prueba se basa principalmente en un nivel de abstracción. Alguien que lee el método de prueba será ver el algoritmo es:

  • crear un widget
  • llamando corrida en el widget
  • hacer valer el código hizo lo que esperamos

Su comprensión del método de ensayo no está enredado por los detalles de la configuración del widget, que es un nivel de abstracción menor que el método de prueba.

La primera versión del método de prueba sigue el patrón Inline Setup. La segunda versión sigue los patrones Creation Method y Delegated Setup.

Generalmente estoy en contra de los comentarios, excepto cuando explican el "por qué" del código. La lectura del Clean Code del tío Bob Martin me convenció de esto. Hay un capítulo sobre comentarios, y hay un capítulo sobre pruebas. Lo recomiendo.

Para obtener más información sobre las mejores prácticas de pruebas automáticas, consulte xUnit Patterns.

+0

Gracias por la recursos adicionales y ayudándome a comprender cómo simplificar las pruebas. Definitivamente voy a leer un poco más sobre el tema. ¡Gracias otra véz! – ddbeck

0

Cuando la prueba falla (que debe ser antes de que pase) debería ver el mensaje de error y poder decir qué pasa. Eso solo sucede si lo planificas de esa manera.

Es totalmente una cuestión de denominación de la clase de prueba, el método de prueba y el mensaje de afirmación. Cuando una prueba falla, y no se puede saber qué ocurre con estas tres pistas, cambie el nombre de algunas cosas o divida algunas clases de prueba.

No sucede si el nombre del dispositivo es ClassXTests y el nombre de la prueba es TestMethodX y el mensaje de error es "esperado verdadero, devuelto falso". Ese es un signo de escritura de prueba descuidada.

La mayoría de las veces no debería tener que leer la prueba o cualquier comentario para saber qué ha sucedido.

Cuestiones relacionadas