¿Cómo llamar la atención del programador en ciertas condiciones?

13

Empecemos con un ejemplo.

Digamos, tengo un método llamado export que depende en gran medida del esquema de base de datos. Y por "depende en gran medida" quiero decir que sé que agregar una nueva columna a una determinada tabla a menudo (muy a menudo) conlleva el correspondiente cambio de método export (por lo general, también debe agregar el nuevo campo a los datos de exportación). / p>

Los programadores a menudo se olvidan de cambiar el método export , ya que no está realmente claro, incluso debería mirar esto. Mi objetivo es forzar al programador explícitamente a tomar una decisión para determinar si olvidó mirar el método export o simplemente no quiere agregar un campo a los datos de exportación. Y estoy buscando la solución de diseño para este problema.

Tengo dos ideas, pero ambas tienen defectos.

Envoltorio inteligente "Leer todo"

Puedo crear el contenedor inteligente que se asegura de que todos los datos se lean explícitamente.

Algo como esto:

def export():
    checker = AllReadChecker.new(table_row)

    name    = checker.get('name')
    surname = checker.get('surname')
              checker.ignore('age') # explicitly ignore the "age" field

    result = [name, surname] # or whatever

    checker.check_now() # check all is read

    return result

Entonces, checker afirma si table_row contiene otros campos que no se leyeron. Pero todo esto parece algo pesado y (tal vez) afecta el rendimiento.

"Compruebe ese método" unittest

Simplemente puedo crear el test de unidad que recuerda el último esquema de la tabla y falla cada vez que se cambia la tabla. En ese caso, el programador vería algo como "no olvide revisar el método export ". Para ocultar la advertencia, el programador podría (o no lo haría, eso es un problema) revisar export y manualmente (ese es otro problema) corregir la prueba agregando nuevos campos en ella.

Tengo otras ideas, pero son demasiado difíciles de implementar o demasiado difíciles de entender (y no quiero que el proyecto se convierta en un enigma).

El problema anterior es solo un ejemplo de la clase más amplia de problemas que encuentro de vez en cuando. Quiero enlazar algunos fragmentos de código y / o infraestructura, por lo que al cambiar uno de ellos, se alerta inmediatamente al programador para que compruebe otro. Por lo general, tiene algunas herramientas simples como extraer lógica común o escribir un test de unidad confiable, pero estoy buscando la herramienta para casos más complejos: tal vez algunos patrones de diseño que ahora conozco.

    
pregunta Vadim Pushtaev 29.09.2015 - 17:49

3 respuestas

11

Está en el camino correcto con su idea de prueba de unidad, pero su implementación es incorrecta.

Si el export está relacionado con el esquema y el esquema cambió, hay dos casos posibles:

  • O bien el export todavía funciona perfectamente bien, porque no se vio afectado por un ligero cambio en el esquema,

  • O se rompe.

En ambos casos, el objetivo de la compilación es rastrear esta posible regresión. Un montón de pruebas, ya sean pruebas de integración, pruebas del sistema, pruebas funcionales o alguna otra cosa, aseguran que su procedimiento export funciona con el esquema actual , independientemente del hecho de que haya cambiado o No desde el cometer anterior. Si esas pruebas pasan, genial. Si fallan, esto es una señal para el desarrollador de que puede haberse perdido algo y una clara indicación de dónde buscar.

¿Por qué está mal tu implementación? Bueno, por varias razones.

  1. No tiene nada que ver con las pruebas unitarias ...

  2. ... y, en realidad, ni siquiera es una prueba.

  3. La peor parte es que arreglar la "prueba" requiere, bueno, realmente cambiar la "prueba", es decir, realizar una operación que no está relacionada por completo con el export .

En cambio, al realizar pruebas reales para el procedimiento export , te aseguras de que el desarrollador reparará el export .

Más generalmente, cuando te encuentras con una situación en la que un cambio en una clase siempre o generalmente requiere un cambio en una clase completamente diferente, es una buena señal de que hiciste mal tu diseño y estás violando el Principio de Responsabilidad Única.

Aunque hablo específicamente sobre las clases, también se aplica más o menos a otras entidades. Por ejemplo, un cambio en el esquema de la base de datos debería reflejarse automáticamente en su código, por ejemplo, a través de generadores de código utilizados por muchos ORM, o al menos debería ser fácilmente localizado: si agrego la columna Description a la tabla Product y uso Sin ORMs o generadores de código, al menos espero hacer un cambio único dentro de la clase Data.Product de la DAL, sin la necesidad de buscar en toda la base de código y encontrar algunas ocurrencias de la clase Product en, digamos, capa de presentación.

Si no puede restringir razonablemente el cambio a una ubicación (ya sea porque está en un caso en el que simplemente no funciona, o porque requiere una gran cantidad de desarrollo), entonces crea un riesgo de < em> regresiones . Cuando cambio la clase A y la clase B en algún lugar de la base del código deja de funcionar, es una regresión.

Las pruebas reducen el riesgo de regresiones y, lo que es mucho más importante, le muestran la ubicación de una regresión. Es por esto que cuando sabe que los cambios en una ubicación causan problemas en una parte completamente diferente del código base, asegúrese de tener suficientes pruebas que activen las alarmas tan pronto como aparezca una regresión en este nivel.

En todos los casos, evite confiar en tales casos solo en los comentarios. Algo como:

// If you change the following line, make sure you also change the corresponding
// 'measure' value in 'Scaffolding.Builder'.

nunca funciona. No solo los desarrolladores no lo leerán en la mayoría de los casos, sino que a menudo terminarán eliminados o alejados de la línea en cuestión y se volverán imposibles de entender.

    
respondido por el Arseni Mourzenko 29.09.2015 - 18:51
3

Me parece que tus cambios no están bien especificados. Supongamos que vive en un lugar que no tiene códigos postales, por lo que no tiene una columna de códigos postales en la tabla de direcciones. Luego se introducen los códigos postales, o comienza a tratar con clientes que viven donde hay códigos postales, y tiene que agregar esta columna a la tabla.

Si el elemento de trabajo simplemente dice "agregar columna de código postal a la tabla de direcciones", entonces sí, la exportación se interrumpirá, o al menos no exportará los códigos postales. Pero ¿qué pasa con la pantalla de entrada que se usa para ingresar códigos postales? ¿El informe que enumera todos los clientes y sus direcciones? Hay toneladas de cosas que deben cambiarse al agregar esta columna. El trabajo de recordar esas cosas es importante: no debes contar con artefactos de código aleatorios para "atrapar" a los desarrolladores para que recuerden.

Cuando se toma la decisión de agregar una columna significativa (es decir, no solo una búsqueda desnormalizada o total en caché u otro valor que no pertenece a una exportación o informe o en una pantalla de entrada) los elementos de trabajo creados deben incluir TODOS los cambios necesarios: agregar la columna, actualizar el script de llenado, actualizar las pruebas, actualizar la exportación, los informes, las pantallas de entrada, etc. Estos no pueden ser asignados (o recogidos por) por la misma persona, pero todos deben hacerse.

A veces los desarrolladores eligen agregar columnas por sí mismos como parte de la implementación de un cambio más grande. Por ejemplo, alguien puede haber escrito un elemento de trabajo para agregar algo a una pantalla de entrada y un informe, sin pensar en cómo se implementa. Si esto sucede mucho, deberá decidir si su sumador de elemento de trabajo necesita conocer los detalles de la implementación (para poder agregar todos los elementos de trabajo correctos) o si los desarrolladores deben saber que el elemento de trabajo La víbora a veces deja las cosas fuera. Si es lo último, entonces necesitas una cultura de "no solo cambies el esquema; detente y piensa en qué más afecta eso".

Si hubiera muchos desarrolladores y esto ocurriera más de una vez, configuraría una alerta de registro para que el líder del equipo u otra persona de alto nivel recibiera una alerta sobre los cambios de esquema. Esa persona podría buscar elementos de trabajo relacionados para lidiar con las consecuencias de su cambio de esquema, y si faltaban los elementos de trabajo, no solo podría agregarlos, sino también educar a quienes los dejaron fuera del plan.

    
respondido por el Kate Gregory 30.09.2015 - 13:28
2

Casi siempre, al crear una exportación también creo una importación correspondiente. Como tengo otras pruebas que completan completamente la estructura de datos que se exportan, luego puedo crear una prueba de unidad de ida y vuelta que compara un original completamente poblado con una copia exportada y luego importada. Si son iguales, entonces la exportación / importación está completa; si no son iguales, la prueba de la unidad falla y sé que el mecanismo de exportación necesita actualizarse.

    
respondido por el Pete Kirkham 30.09.2015 - 12:05

Lea otras preguntas en las etiquetas