unitarias - unit test c#
Normas de escritura para pruebas unitarias (10)
- Intente utilizar tan pocas afirmaciones afirmativas por método de prueba como sea posible. Esto asegura que el propósito de la prueba esté bien definido.
- Sé que esto será controvertido, pero no pruebes el compilador: el tiempo dedicado a probar los adaptadores y mutadores de Java Bean se usa mejor escribiendo otras pruebas.
- Intente, cuando sea posible, usar TDD en lugar de escribir sus pruebas después de su código.
Planeo introducir un conjunto de estándares para escribir pruebas unitarias en mi equipo. Pero, ¿qué incluir?
Estas dos publicaciones ( Pruebas unitarias nominando las mejores prácticas y Mejores prácticas para las dependencias del sistema de archivos en las pruebas de unidad / integración ) ya me han dado algo de reflexión.
Otros dominios que deberían cubrirse en mis estándares deberían ser cómo se configuran las clases de prueba y cómo organizarlas. Por ejemplo, si tiene una clase llamada OrderLineProcessor, debe haber una clase de prueba llamada OrderLineProcessorTest. Si hay un método llamado Process () en esa clase, entonces debe haber una prueba llamada ProcessTest (tal vez más para probar diferentes estados).
¿Alguna otra cosa para incluir?
¿Su empresa tiene estándares para pruebas unitarias?
EDITAR: estoy usando Visual Studio Team System 2008 y desarrollo en C # .Net
Descubrí que la mayoría de las convenciones de prueba se pueden aplicar mediante el uso de una clase base estándar para todas sus pruebas. Obligar al probador a anular los métodos para que todos tengan el mismo nombre.
También defiendo el estilo de prueba Arrange-Act-Assert (AAA) ya que puede generar documentación bastante útil de sus pruebas. También lo obliga a considerar qué comportamiento espera debido al estilo de denominación.
Los usuarios de IDE completos encontrarán que "algunos de ellos" tienen un soporte bastante detallado para crear pruebas en un patrón específico. Dada esta clase:
public class MyService {
public String method1(){
return "";
}
public void method2(){
}
public void method3HasAlongName(){
}
}
Cuando presiono ctrl-shift-T en intellidia IDEA obtengo esta clase de prueba después de responder 1 cuadro de diálogo:
public class MyServiceTest {
@Test
public void testMethod1() {
// Add your code here
}
@Test
public void testMethod2() {
// Add your code here
}
@Test
public void testMethod3HasAlongName() {
// Add your code here
}
}
Así que es posible que desee examinar de cerca el soporte de la herramienta antes de escribir sus normas.
Otro elemento que puede poner en sus estándares es tratar de mantener el tamaño de su unidad de prueba pequeña. Ese es el verdadero método de prueba en sí. A menos que esté realizando una prueba de unidad de integración completa, por lo general no es necesario realizar pruebas de unidades grandes, como, por ejemplo, más de 100 líneas. Te daré mucho en caso de que tengas mucha configuración para llegar a tu única prueba. Sin embargo, si lo hace, quizás deba refactorizarlo.
La gente también habla de refactorizar el código para asegurarse de que las personas se den cuenta de que las pruebas unitarias también son código. Entonces refactor, refactor, refactor.
Encuentro que el mayor problema en los usos que he visto es que la gente no suele reconocer que desea que las pruebas de su unidad sean livianas y ágiles. Después de todo, no quieres una bestia monolítica para tus pruebas. Con esto en mente, si tiene un método que está tratando de probar, no debe probar todas las rutas posibles en una prueba unitaria. Debe tener pruebas de unidades múltiples para dar cuenta de cada ruta posible a través del método.
Sí, si realiza las pruebas de su unidad correctamente, en promedio debería tener más líneas de código de prueba de unidad que su aplicación. Si bien esto parece mucho trabajo, le ahorrará mucho tiempo al final, cuando llegue el momento del inevitable cambio de requisitos comerciales.
Probablemente deberías echarle un vistazo a la serie "Pragmatic Unit Testing". Esta es la versión C # pero hay otra para Java.
Con respecto a su especificación, no me exageraría. Tienes un buen comienzo allí: las convenciones de nomenclatura son muy importantes. También requerimos que la estructura del directorio coincida con el proyecto original. La cobertura también debe extenderse a casos límite y valores ilegales (buscando excepciones). Esto es obvio, pero su especificación es el lugar para escribirlo para ese argumento que inevitablemente tendrá en el futuro con el tipo que no quiere probar a alguien que está pasando un valor ilegal. Pero no hagas las especificaciones más que unas pocas páginas o nadie las usará para una tarea que sea tan dependiente del contexto.
Actualización: No estoy de acuerdo con el Sr. Potato Head sobre una sola afirmación por prueba unitaria. Suena bastante bien en teoría, pero, en la práctica, conduce a cargas de pruebas en su mayoría redundantes o a personas que hacen muchísimo trabajo en la configuración y el derribo que deberían probarse.
Si está utilizando herramientas de la familia de Junit (OCunit, SHunit, ...), los nombres de las pruebas ya siguen algunas reglas.
Para mis pruebas, uso etiquetas personalizadas de Doxygen para reunir su documentación en una página específica.
Uso un inglés casi llano para los nombres de funciones de prueba de mi unidad. Ayuda a definir lo que hacen exactamente:
TEST( TestThatVariableFooDoesNotOverflowWhenCalledRecursively )
{
/* do test */
}
Uso C ++ pero la convención de nomenclatura se puede usar en cualquier lugar.
Sigo el estilo BDD de TDD. Ver: http://blog.daveastels.com/files/BDD_Intro.pdf http://dannorth.net/introducing-bdd http://behaviour-driven.org/Introduction
En resumen, esto significa que
Las pruebas no se consideran como "pruebas", sino como especificaciones del comportamiento del sistema (en lo sucesivo denominadas "especificaciones"). La intención de las especificaciones no es verificar que el sistema funcione en todas las circunstancias. Su intención es especificar el comportamiento y conducir el diseño del sistema.
Los nombres de los métodos de especificación se escriben como oraciones completas en inglés. Por ejemplo, las especificaciones para una pelota podrían incluir "la pelota es redonda" y "cuando la pelota golpea un piso, luego rebota".
No existe una relación 1: 1 forzada entre las clases de producción y las clases de especificación (y la generación de un método de prueba para cada método de producción sería una locura). En cambio, hay una relación 1: 1 entre el comportamiento del sistema y las especificaciones.
Hace algún tiempo escribí el tutorial de TDD (donde empiezas a escribir un juego de Tetris usando las pruebas provistas) que muestra este estilo de pruebas de escritura como especificaciones. Puedes descargarlo desde http://www.orfjackal.net/tdd-tutorial/tdd-tutorial_2008-09-04.zip. Las instrucciones sobre cómo hacer TDD / BDD todavía faltan en ese tutorial, pero el código de ejemplo está listo , para que pueda ver cómo se organizan las pruebas y escribir el código que las pasa.
Notarás que en este tutorial se nombran las clases de producción, como Board, Block, Piece y Tetrominoe, que se centran en los conceptos de un juego Tetris. Pero las clases de prueba se centran en el comportamiento del juego Tetris: FallingBlocksTest, RotatingPiecesOfBlocksTest, RotatingTetrominoesTest, FallingPiecesTest, MovingAFallingPieceTest, RotatingAFallingPieceTest etc.
Eche un vistazo a Michael Feathers en lo que es una prueba unitaria (o lo que hace que las pruebas unitarias sean malas pruebas unitarias)
Eche un vistazo a la idea de "Arrange, Act, Assert", es decir, la idea de que una prueba solo hace tres cosas, en un orden fijo:
- Organice los datos de entrada y las clases de procesamiento necesarias para la prueba
- Realiza la acción bajo prueba
- Pruebe los resultados con uno o más asertos . Sí, puede ser más de una afirmación, siempre y cuando todos trabajen para probar la acción que se realizó.
Eche un vistazo al desarrollo impulsado por el comportamiento para una forma de alinear los casos de prueba con los requisitos.
Además, mi opinión sobre los documentos estándar de hoy es que no debes escribirlos a menos que tengas que hacerlo: hay muchos recursos disponibles que ya están escritos. Enlace a ellos en lugar de repetir su contenido. Proporcione una lista de lectura para desarrolladores que quieran saber más.
Asegúrese de incluir lo que no es una unidad de prueba. Ver: ¿Qué no probar cuando se trata de pruebas unitarias?
Incluya una guía para que las pruebas de integración estén claramente identificadas y se puedan ejecutar por separado de las pruebas unitarias. Esto es importante, porque puede terminar con un conjunto de pruebas de "unidad" que son realmente lentas si las pruebas unitarias se mezclan con otros tipos de pruebas.
Verifique esto para obtener más información al respecto: ¿Cómo puedo mejorar mis pruebas junit ... especialmente la segunda actualización?