2009-05-04 10 views
10

Hace un tiempo que tengo preparado el "Code Complete" de Mcconnell. Ahora lo leí de nuevo en Hunt & Thomas '"The Pragmatic Programmer": ¡Use aserciones! Nota: No afirmaciones de prueba de unidad, me refiero a Debug.Assert().¿Las Pruebas Unitarias hacen que Debug.Assert() sea innecesario?

Después de las preguntas SO When should I use Debug.Assert()? y When to use assertion over exceptions in domain classes afirmaciones son útiles para el desarrollo, porque las situaciones "imposibles" se pueden encontrar bastante rápido. Y parece que se usan comúnmente. Por lo que entendí las afirmaciones, en C# a menudo se usan para verificar variables de entrada para valores "imposibles".

Para mantener las pruebas unitarias concisas y aisladas tanto como sea posible, doy clases y métodos con null sy entrada ficticia "imposible" (como una cadena vacía).

Dichas pruebas documentan explícitamente, que no dependen de alguna entrada específica. Nota: Estoy practicando lo que Meszaros "Patrones de prueba de xUnit" está describiendo como Minimal Fixture.

Y ese es el punto: si tuviera una afirmación protegiendo estas entradas, explotarían mis pruebas unitarias.

Me gusta la idea de la Programación Assertative, pero, por otro lado, no es necesario forzarla. Actualmente no puedo pensar en ningún uso para Debug.Assert(). Tal vez hay algo que extraño? ¿Tiene alguna sugerencia, donde podrían ser realmente útil? Tal vez solo sobreestimar la utilidad de las afirmaciones? ¿O tal vez mi forma de prueba necesita ser revisada?

Edit: Best practice for debug Asserts during Unit testing es muy similar, pero no responde la pregunta que me molesta: ¿Debería preocuparme por Debug.Assert() en C# si pruebo como lo he descrito? En caso afirmativo, ¿en qué situación son realmente útil? En mi punto de vista actual, tales Pruebas Unitarias harían innecesario el Debug.Assert().

Otro punto: si realmente crees que esta es una pregunta duplicada, solo publica un comentario.

+1

Tenga en cuenta que el marco de prueba de Visual Studio * no * trata una depuración. Confirma la falla como una falla de prueba de la unidad. – yoyo

Respuesta

5

En teoría, tiene razón: las pruebas exhaustivas hacen que las afirmaciones sean redundantes. En teoria. En parctice, todavía son útiles para depurar sus pruebas, y para detectar intentos de futuros desarrolladores que podrían tratar de usar interfaces no de acuerdo con su semántica prevista.

En resumen, solo sirven para un propósito diferente de las pruebas unitarias. Están ahí para detectar errores que, por su propia naturaleza, no se van a realizar al escribir pruebas unitarias.

Recomendaría mantenerlos, ya que ofrecen otro nivel de protección contra errores del programador.

También son un mecanismo local de protección contra errores, mientras que las pruebas unitarias son externas al código que se está probando. Es mucho más fácil "inadvertidamente" desactivar las pruebas unitarias cuando está bajo presión que desactivar todas las afirmaciones y verificaciones de tiempo de ejecución en un fragmento de código.

+0

"... para intentos de captura por parte de futuros desarrolladores que podrían intentar usar interfaces que no estén de acuerdo con su semántica prevista". Mmm ... Puedo pensar en un nicho, en el que son útiles: cuando la semántica de una interfaz no es aparente, poner una afirmación hace que esto sea muy explícito. –

+0

Pensando de nuevo sobre esto, tiene mucho sentido: usar aserciones de esta manera es beneficioso durante el desarrollo y tiene como objetivo descubrir errores de programación (más bien un error de entrada de un usuario/base de datos/servicio web/...) –

+0

Exactamente. Es otro aspecto de la programación por contrato, donde el contrato no puede ejecutarse en tiempo de compilación, pero el tiempo de ejecución puede detectar violaciones. –

2

Si está realizando pruebas exhaustivas de unidades que cubren todos los casos de bordes impares que pueda encontrar, entonces no creo que vaya a encontrar afirmaciones muy útiles. La mayoría de las personas que no realizan test de unidades colocan aserciones para establecer restricciones similares a medida que capta sus pruebas.

+0

Para mí eso significa: las afirmaciones son buenas, cuando no hay pruebas de unidades en su lugar. Aunque no pueden reemplazar las pruebas unitarias, es mejor que nada. –

+1

Francamente, diría que las afirmaciones (en la forma en que la mayoría de las personas las usan) son casi lo mismo que las pruebas unitarias. La gente generalmente los incluye para verificar que la entrada de una función es algo que pueden manejar, y que el resultado de una función es lo que esperan que sea, que es exactamente lo que su prueba de unidad debería hacer si prueba esa función. . La mayor diferencia es que su suite de pruebas unitarias a menudo no está exactamente en el mismo lugar que su código, mientras que las aserciones están en línea. – mquander

+0

Por supuesto, las afirmaciones a veces también verifican cosas más detalladas que las pruebas unitarias, como asegurarse de que la memoria se haya asignado correctamente o que la conexión se haya realizado correctamente. – mquander

3

Estoy usando pruebas de unidad y aserciones, para diferentes propósitos.

La prueba unitaria es una experimentación automatizada que muestra que su programa (y sus partes) funcionan como se especifica. Al igual que en las matemáticas, la experimentación no es una prueba siempre que no puedas probar todas las combinaciones posibles de datos. Nada demuestra eso mejor que el hecho de que incluso con las pruebas unitarias, tu código tendrá errores. No muchos, pero los tendrá.

Las afirmaciones son para detectar situaciones dudosas durante el tiempo de ejecución que normalmente no deberían suceder. Tal vez hayas oído hablar de precondiciones, postcondiciones, invariables de bucle y cosas por el estilo. En el mundo real, no solemos pasar por el proceso formal de probar realmente (mediante una lógica formal) que un fragmento de código proporciona las condiciones posteriores especificadas si se cumplen las condiciones previas. Esa sería una prueba matemática real, pero a menudo no tenemos tiempo para hacer eso para cada método. Sin embargo, al verificar si se cumplen las condiciones previas y las postcondiciones, podemos detectar problemas en una etapa mucho más temprana.

+0

No hago todo el proceso de prueba del 100%, pero pensé que cubrir las condiciones previas y las postcondiciones era lo más importante; pruebas automatizadas que realmente cubrieron todos sus casos extremos, alimentaron todos los posibles tipos de datos que podrían entrar, y probaron todos los datos que salieron. – mquander

+1

Las afirmaciones no se hacen explícitamente para verificar condiciones previas y posteriores, pero siguen siendo útiles para emularla. Ese es otro punto donde las afirmaciones son útiles. –

1

Creo que la idea detrás de Unit Testing en este caso es trasladar esas afirmaciones a los casos de prueba para garantizar que, en lugar de tener Debug.Asert (...) su código bajo prueba lo maneje sin tirar (o asegura que está vomitando correctamente).

4

Por lo general, veo que se utiliza para verificar la cordura en el estado interno en lugar de cosas como la comprobación de argumentos.

IMO las entradas a una API sólida deben protegerse mediante comprobaciones que permanecen en su lugar independientemente del tipo de compilación. Por ejemplo, si un método público espera un argumento que es un número entre 5 y 500, debe protegerse con una ArgumentOutOfRangeException. Fallo rápido y fallar a menudo usando excepciones en lo que a mí concierne, particularmente cuando un argumento se empuja a algún lugar y se usa mucho más tarde.

Sin embargo, en lugares donde el estado transitorio interno se está verificando (por ejemplo, verificar que algún estado intermedio se encuentre dentro de límites razonables durante un ciclo), parece que Debug.Assert es más adecuado. ¿Qué más debe hacer cuando su algoritmo ha fallado a pesar de tener argumentos válidos pasados? ¿Lanzar una EpicFailException? :) Creo que aquí es donde Debug.Assert sigue siendo útil.

Todavía estoy indeciso sobre el mejor equilibrio entre los dos. Dejé de usar Debug. Afirma mucho en C# desde que comencé las pruebas unitarias, pero todavía hay un lugar para IMO. Ciertamente no los usaría para verificar la corrección en el uso de la API, pero la comprobación de la cordura es difícil de llegar a los lugares. Por supuesto.

El único inconveniente es que pueden aparecer y detener NUnit, pero puede escribir un plugin NUnit para detectarlos y fallar cualquier prueba que desencadene una afirmación.

+1

Lol en EpicFailException ... Tendré que usar eso en mi código (combinado con Over9000Exception). –

+0

"por ejemplo, verificar que algún estado intermedio se encuentre dentro de límites razonables durante un bucle" ... ¡Afirmaciones para verificar invariantes de bucle! Ese es un buen punto. Eso es algo que realmente no puedo verificar con pruebas unitarias (al menos no con más o menos burla/stubbing e introduciendo algunas variables espías). –

+0

Esas ventanas emergentes son bastante molestas ... no sé qué pasa, cuando ocurre una afirmación cuando sucede en el servicio de Windows ... podría valer otra pregunta ;-) –

Cuestiones relacionadas