Cómo mejorar en la prueba de su propio código

45

Soy un desarrollador de software relativamente nuevo, y una de las cosas que creo que debo mejorar es mi capacidad para probar mi propio código. Cada vez que desarrollo una nueva funcionalidad, me resulta muy difícil seguir todos los caminos posibles para poder encontrar errores. Tiendo a seguir el camino donde todo funciona. Sé que este es un problema bien conocido que tienen los programadores, pero no tenemos evaluadores en mi empleador actual y mis colegas parecen ser muy buenos en esto.

En mi organización, no realizamos pruebas de desarrollo ni pruebas de unidad. Me ayudaría mucho, pero no es probable que esto cambie.

¿Qué piensan ustedes que podría hacer para superar esto? ¿Qué enfoque utiliza al probar su propio código?

    
pregunta Fran Sevillano 29.08.2011 - 14:53

14 respuestas

20

El trabajo de un codificador es construir cosas.

El trabajo de un probador es romper cosas.

Lo más difícil es romper cosas que acabas de construir. Solo tendrás éxito al superar esta barrera psicológica.

    
respondido por el mouviciel 29.08.2011 - 15:00
14

Franciso, voy a hacer algunas suposiciones aquí, basadas en lo que has dicho:

"No hacemos TDD ni pruebas de unidad. Me ayudaría mucho, pero no es probable que esto cambie".

A partir de esto, sospecho que su equipo no le da mucho valor a las pruebas o la administración no le dará tiempo al equipo para tratar de ordenar el código existente y mantener "deuda técnica a un mínimo.

Primero, debe convencer a su equipo / administración sobre el valor de las pruebas. Ser diplomatico Si la administración mantiene a su equipo avanzando, necesita mostrarles algunos datos, como la tasa de defectos para cada lanzamiento. El tiempo dedicado a reparar los defectos se podría invertir mejor en otras cosas, como mejorar la aplicación y hacerla más adaptable a los requisitos futuros.

Si el equipo y la administración en general se muestran apáticos a la hora de arreglar el código y no se siente satisfecho con él, es posible que deba buscar otro lugar para trabajar, a menos que pueda convencerlos como dije. He encontrado este problema en diferentes grados en todos los lugares donde he trabajado. Podría ser cualquier cosa, desde la falta de un modelo de dominio adecuado hasta una mala comunicación en el equipo.

Cuidar de su código y la calidad del producto que desarrolla es un buen atributo y uno que siempre desea fomentar en otras personas.

    
respondido por el Desolate Planet 29.08.2011 - 15:17
11

Si codifica en C, Objective-C o C ++, puede usar el CLang Static Analyzer para criticar su fuente sin realmente ejecutandolo.

Hay algunas herramientas de depuración de memoria disponibles: ValGrind, Guard Malloc en Mac OS X, Electric Fence en * NIX.

Algunos entornos de desarrollo ofrecen la opción de usar un asignador de memoria de depuración, que hace cosas como rellenar las páginas recién asignadas y las páginas recién liberadas con basura, detectar la liberación de punteros no asignados y escribir algunos datos antes y después de cada bloque de montón, con se llama al depurador si el patrón conocido de esos datos cambia alguna vez.

Un tipo en Slashdot dijo que obtuvo mucho valor de una sola vez nueva línea de fuente en un depurador. "Eso es todo" dijo. No siempre sigo su consejo, pero cuando lo tengo me ha sido muy útil. Incluso si no tiene un caso de prueba que estimule una ruta de código poco común, puede girar una variable en su depurador para tomar dichas rutas, por ejemplo, asignando algo de memoria y luego utilizando el depurador para establecer su nuevo puntero en NULL en lugar de dirección de memoria, luego pasar por el controlador de errores de asignación.

Utilizar aserciones: la macro assert () en C, C ++ y Objective-C. Si su idioma no proporciona una función de afirmación, escríbala usted mismo.

Use afirmaciones generosamente, luego déjelas en su código. Llamo a assert () "La prueba que sigue probando". Los uso con más frecuencia para verificar las condiciones previas en el punto de entrada de la mayoría de mis funciones. Esa es una parte de "Programación por contrato", que está incorporada en el lenguaje de programación de Eiffel. La otra parte es postcondiciones, es decir, usar assert () en los puntos de retorno de la función, pero me parece que no obtengo tanto kilometraje como precondiciones.

También puede usar aserción para verificar invariantes de clase. Si bien no se requiere estrictamente que ninguna clase tenga invariable, las clases más sensiblemente diseñadas las tienen. Una clase invariante es una condición que siempre es verdadera, excepto dentro de las funciones miembro, que podrían colocar temporalmente su objeto en un estado inconsistente. Tales funciones siempre deben restaurar la consistencia antes de regresar.

Por lo tanto, cada función miembro podría verificar el invariante al ingresar y salir, y la clase podría definir una función llamada CheckInvariant que cualquier otro código podría llamar en cualquier momento.

Use una herramienta de cobertura de código para verificar qué líneas de su fuente realmente se están probando, luego diseñe pruebas que estimulen las líneas no probadas. Por ejemplo, puede verificar los manejadores de poca memoria ejecutando su aplicación dentro de una VM que está configurada con poca memoria física, y no con un archivo de intercambio o uno muy pequeño.

(Por alguna razón, nunca tuve conocimiento de ello, aunque el BeOS podía ejecutarse sin un archivo de intercambio, era muy inestable de esa manera. Dominic Giampaolo, quien escribió el sistema de archivos BFS, me instó a que nunca ejecutara el BeOS sin intercambio. no veo por qué eso debería importar, pero debe haber sido algún tipo de artefacto de implementación.)

También debe probar la respuesta de su código a los errores de E / S. Intente almacenar todos sus archivos en un recurso compartido de red, luego desconecte el cable de red mientras su aplicación tiene una gran carga de trabajo. De manera similar, desconecte el cable, o apague su red inalámbrica, si se está comunicando a través de una red.

Una cosa que me resulta particularmente irritante son los sitios web que no tienen un código Javascript robusto. Las páginas de Facebook cargan docenas de pequeños archivos de Javascript, pero si alguno de ellos no se descarga, la página se rompe. Solo tiene que haber alguna forma, ya sea para proporcionar cierta tolerancia a fallos, por ejemplo, volviendo a intentar una descarga, o para proporcionar algún tipo de respuesta razonable cuando algunos de sus scripts no se descargaron.

Intente matar su aplicación con el depurador o con "kill -9" en * NIX mientras está en medio de escribir un archivo grande e importante. Si su aplicación está bien diseñada, todo el archivo se escribirá o no se escribirá en absoluto, o tal vez si solo está escrito parcialmente, lo que se escribe no se corromperá, y los datos guardados serán completamente utilizables por la aplicación al volver a leer el archivo.

Las bases de datos

siempre tienen E / S de disco tolerante a fallos, pero casi ningún otro tipo de aplicación lo tiene. Si bien los sistemas de archivos registrados por diario evitan la corrupción del sistema de archivos en caso de falla de energía o fallas, no hacen nada para evitar la corrupción o la pérdida de datos del usuario final. Esa es la responsabilidad de las aplicaciones de usuario, pero casi ninguna otra que las bases de datos implementa la tolerancia a fallos.

    
respondido por el Mike Crawford 29.08.2011 - 15:23
10

Cuando miro a probar mi código, generalmente paso por una serie de procesos de pensamiento:

  1. ¿Cómo puedo dividir este "cosito" en trozos de tamaño comprobable? ¿Cómo puedo aislar solo lo que quiero probar? ¿Qué colillas / simulacros debo crear?
  2. Para cada fragmento: ¿Cómo pruebo este fragmento para asegurarme de que responde correctamente a un conjunto razonable de entradas correctas?
  3. Para cada fragmento: ¿Cómo pruebo que el fragmento responda correctamente a entradas incorrectas (punteros NULL, valores no válidos)?
  4. ¿Cómo pruebo los límites (por ejemplo, desde dónde van los valores de firmado a sin signo, de 8 bits a 16 bits, etc.)?
  5. ¿Qué tan bien cubren el código mis pruebas? ¿Hay alguna condición que me perdí? [Este es un excelente lugar para las herramientas de cobertura de código.] Si hay un código que se perdió y que nunca se puede ejecutar, ¿es necesario que esté allí? [¡Esa es otra pregunta!

La forma más fácil que he encontrado para hacer esto es desarrollar mis pruebas junto con mi código. Tan pronto como he escrito incluso un fragmento de código, me gusta escribir una prueba para ello. Tratar de realizar todas las pruebas después de haber codificado varios miles de líneas de código con complejidad de código ciclomático no trivial es una pesadilla. Agregar una o dos pruebas más después de agregar algunas líneas de código es realmente fácil.

Por cierto, solo porque la empresa en la que trabajas y / o tus colegas no realizan Unit Test o TDD, no significa que no puedas probarlos, a menos que estén específicamente prohibidos. Quizás usarlos para crear un código sólido sea un buen ejemplo para otros.

    
respondido por el jwernerny 29.08.2011 - 15:21
5

Además del consejo dado en las otras respuestas, sugeriría usar análisis estático (Wikipedia tiene una lista de una serie de herramientas de análisis estático para varios idiomas ) para encontrar defectos potenciales antes de que comiencen las pruebas, así como para supervisar algunos parámetros que se relacionan para la capacidad de prueba del código, como complejidad ciclomática , Mediciones de complejidad de Halstead , y cohesión y acoplamiento (puede medirlas con entrada y salida).

Encontrar el código de prueba difícil y hacer que sea más fácil de hacer le ayudará a escribir casos de prueba. Además, la detección temprana de defectos agregará valor a todas sus prácticas de control de calidad (que incluyen pruebas). A partir de aquí, familiarizarse con las herramientas de prueba de unidad y las herramientas de simulación le facilitarán la implementación de sus pruebas.

    
respondido por el Thomas Owens 29.08.2011 - 15:27
3

Puedes ver el posible uso de Truth Tables para ayudarte a definir todas las rutas posibles en tu código. Es imposible tener en cuenta todas las posibilidades en funciones complejas, pero una vez que haya establecido su manejo para todas las rutas conocidas, puede establecer un manejo para el otro caso.

Sin embargo, la mayor parte de esta habilidad en particular se aprende por experiencia. Después de haber usado un cierto marco durante un período de tiempo significativo, comienza a ver los patrones y las características del comportamiento que le permitirán ver un fragmento de código y ver dónde un pequeño cambio podría causar un error importante. La única forma en que puedo pensar para aumentar tu aptitud en esto es la práctica.

    
respondido por el Joel Etherton 29.08.2011 - 15:01
3

Si, como usted dijo, no necesita pruebas de unidad, no veo un enfoque mejor que intentar romper su propio código manualmente.

Intente llevar su código a los límites . Por ejemplo, intente pasar variables a una función que exceda los límites del límite. ¿Tiene una función que se supone que filtra la entrada del usuario? Intenta ingresar diferentes combinaciones de caracteres.

Considere el punto de vista del usuario . Intente ser uno de los usuarios que usarán su aplicación o biblioteca de funciones.

    
respondido por el Jose Faeti 29.08.2011 - 15:08
3
  

pero no tenemos evaluadores en mi empleador actual y mis colegas parecen ser muy buenos en esto

Sus colegas deben ser verdaderamente excepcionales para no seguir el TDD o la prueba de unidad y nunca generar errores, por lo que, en algún nivel, dudo que no estén realizando ninguna prueba de unidad por sí mismos.

Supongo que sus colegas están realizando más pruebas de las que están teniendo en cuenta, pero como la gerencia no conoce este hecho, la organización se ve afectada debido a que la administración tiene la impresión de que no se realizan las pruebas verdaderas y los números de error son bajos, por lo que las pruebas no son importantes y el tiempo no se programará para ello.

Hable con sus colegas y trate de averiguar qué tipo de pruebas de unidad están haciendo y emular eso. En un momento posterior, puede crear un prototipo de mejores formas de prueba de unidad y atributos TDD e introducir lentamente estos conceptos al equipo para facilitar su adopción.

    
respondido por el maple_shaft 29.08.2011 - 15:53
2
  • Escriba sus pruebas antes de escribir su código.
  • Cada vez que arregles un error que no fue detectado por una prueba, escribe una prueba para detectar ese error.

Debería poder obtener cobertura sobre lo que escribe, incluso si su organización no tiene cobertura completa. Como tantas otras cosas en la programación, la experiencia de hacerlo una y otra vez es una de las mejores maneras de ser eficiente en ello.

    
respondido por el Jeff Ferland 29.08.2011 - 17:09
2

Además de todos los otros comentarios, ya que dice que sus colegas son buenos para escribir pruebas que no son de buena calidad, ¿por qué no pedirles que se unan a usted para escribir algunas pruebas?

La mejor manera de aprender es ver cómo se hace y extraer lo que aprendes de eso.

    
respondido por el Jim Munro 29.08.2011 - 18:22
2

Pruebas de caja negra! Debes crear tus clases / métodos teniendo en cuenta las pruebas. Sus pruebas deben basarse en la especificación del software y deben estar claramente definidas en su diagrama de secuencia (a través de casos de uso).

Ahora, ya que es posible que no desee realizar un desarrollo guiado por pruebas ...

Coloque la validación de entrada en todos los datos entrantes; no confíes en nadie El .net framework tiene muchas excepciones basadas en argumentos inválidos, referencias nulas y estados inválidos. Ya deberías estar pensando en utilizar la validación de entrada en la capa UI, por lo que es el mismo truco en el software intermedio.

Pero realmente deberías estar haciendo algún tipo de prueba automatizada; esas cosas salvan vidas.

    
respondido por el user15279 29.08.2011 - 19:53
2

En mi experiencia

Unidad de prueba, si no es completamente automática, es inútil. Es más como un jefe de pelo puntiagudo podría comprar. ¿Por qué ?, porque la Unidad de prueba le prometió ahorrar tiempo (y dinero) automatizando algunos procesos de prueba. Pero, algunas herramientas de unidad de prueba hacen lo contrario, obliga a los programadores a trabajar de alguna manera extraña y obliga a otras a crear pruebas de extensión excesiva. La mayoría de las veces, no ahorrará horas de trabajo, sino que aumentará el tiempo de traslado de QA a desarrollador.

UML es otra pérdida de tiempo. una sola pizarra + lápiz podría hacer lo mismo, más barato y rápido.

BTW, ¿Cómo ser bueno en la codificación (y evitar errores)?

  • a) atomicidad. Una función que hace una simple (o algunas tareas simples). Debido a que es fácil de entender, es fácil de rastrear y es fácil de resolverlo.

  • b) Homología. Si, por ejemplo, llama a una base de datos utilizando un procedimiento de almacenamiento, hágalo por el resto del código.

  • c) Identifique, reduzca y aísle el "código creativo". La mayor parte del código es bastante copia y amp; pegar. El código creativo es lo opuesto, un código nuevo y que actúa como un prototipo, puede fallar. Este código es propenso a errores de lógica, por lo que es importante reducirlo, aislarlo e identificarlo.

  • d) El código "Thin ice" es el código que usted sabe que es "incorrecto" (o potencialmente peligroso) pero que aún se necesita, por ejemplo, un código inseguro para un proceso de múltiples tareas. Evítalo si puedes.

  • e) Evite el código de recuadro negro, esto incluye el código que usted no realiza (por ejemplo el marco) y la expresión regular. Es fácil pasar por alto un error con este tipo de código. Por ejemplo, trabajé en un proyecto usando Jboss y no encontré uno sino 10 errores en Jboss (usando la última versión estable), fue un PITA para encontrarlos. Evite especialmente la hibernación, ya que oculta la implementación, de ahí los errores.

  • f) agrega comentarios en tu código.

  • g) la entrada del usuario como fuente de errores. identificarlo Por ejemplo, la inyección SQL se debe a una entrada del usuario.

  • h) Identifique el elemento defectuoso del equipo y separe la tarea asignada. Algunos programadores son propensos a cambiar el código.

  • i) Evite el código innecesario. Si, por ejemplo, la clase necesita Interfaz , úsela, de lo contrario evite agregar código irrelevante.

a) yb) son claves. Por ejemplo, tuve un problema con un sistema, cuando hice clic en un botón (guardar) no se guardó el formulario. Entonces hice una lista de verificación:

  • el botón funciona? ... sí.
  • ¿
  • la base de datos almacena algo? no, por lo que el error estaba en un paso intermedio.
  • entonces, ¿la clase que almacena en la base de datos funciona? no < - ok, encontré el error. (Estaba relacionado con el permiso de la base de datos). Luego verifiqué no solo este procedimiento, sino también todos los procedimientos que hacen lo mismo (debido a la homología del código). Me tomó 5 minutos rastrear el error y 1 minuto para resolverlo (y muchos otros errores).

Y una nota al margen

  

La mayoría de las veces, chupar QA (como una sección separada de Dev), es inútil si no se trabajan en el proyecto. Hacen alguna prueba genérica y nada más. Ellos son incapaces de identificar la mayoría de los errores de lógica. En mi caso, estaba trabajando en un banco prestigioso, un programador completó un código y luego lo envió a QA. QA aprobó el código y se puso en producción ... luego el código falló (un error épico), ¿sabe quién fue el culpable? Sí, el programador.

    
respondido por el magallanes 30.08.2011 - 14:57
2

Un probador y un programador enfrentan el problema desde diferentes ángulos, pero ambos roles deben probar completamente la funcionalidad y encontrar errores. Donde los roles difieren es en foco. Un probador clásico ve la aplicación solo desde el exterior (es decir, una caja negra). Son expertos en los requisitos funcionales de la aplicación. Se espera que un programador sea un experto tanto en los requisitos funcionales como en el código (pero tiende a centrarse más en el código).

(Depende de la organización si se espera explícitamente que los programadores sean expertos en requisitos. En cualquier caso, la expectativa implícita está ahí: si usted diseña algo incorrecto, usted, no la persona encargada de los requisitos, recibe la culpa.)

Este doble rol de experto está sobrecargando a la mente del programador y, a excepción de los más experimentados, puede disminuir la competencia en los requisitos. Encuentro que debo cambiar mentalmente para considerar a los usuarios de la aplicación. Esto es lo que me ayuda:

  1. Depuración ; establezca puntos de interrupción en el código y ejecute la aplicación. Una vez que llegue a un punto de interrupción, recorra las líneas a medida que interactúa con la aplicación.
  2. Pruebas automatizadas ; Escriba el código que prueba su código. Esto solo ayuda en los niveles por debajo de la interfaz de usuario.
  3. Conoce a tus probadores ; Es posible que conozcan la aplicación mejor que tú, así que aprende de ellos. Pregúnteles cuáles son las debilidades de su aplicación y qué tácticas utilizan para encontrar errores.
  4. Conoce a tus usuarios ; Aprende a caminar en los zapatos de tus usuarios. Los requisitos funcionales son la huella digital de sus usuarios. A menudo hay muchas cosas que sus usuarios saben acerca de la aplicación que pueden no aparecer claramente en los requisitos funcionales. A medida que entienda mejor a sus usuarios (la naturaleza de su trabajo en el mundo real y cómo se supone que su aplicación debe ayudarlos), comprenderá mejor lo que se supone que debe ser la aplicación.
respondido por el Matthew Rodatus 31.08.2011 - 14:27
2

Creo que quieres trabajar en dos frentes. Una es política, lograr que su organización adopte las pruebas en algún nivel (con la esperanza de que con el tiempo adopten más). Hable con los ingenieros de control de calidad fuera de su lugar de trabajo. Encuentre listas de libros de control de calidad . Busque en los artículos relevantes de wikipedia . Familiarícese con los principios y prácticas de control de calidad. Aprender esto lo preparará para presentar el caso más convincente que pueda en su organización. Los buenos departamentos de control de calidad existen y agregan un valor considerable a sus organizaciones.

Como desarrollador individual, adopte estrategias para usar en su propio trabajo. Utilice TDD usted mismo mediante el desarrollo conjunto del código y las pruebas. Mantenga las pruebas claras y bien mantenidas. Si se le pregunta por qué está haciendo esto, puede decir que está previniendo las regresiones y mantiene su proceso de pensamiento mejor organizado (ambos serán verdad). Hay un arte para escribir código comprobable , apréndelo. Sea un buen ejemplo para sus compañeros desarrolladores.

En parte, me estoy predicando a mí mismo aquí, porque hago mucho menos de lo que debería.

    
respondido por el Will Ware 01.09.2011 - 05:05

Lea otras preguntas en las etiquetas