Estoy trabajando con el siguiente sistema:
Network Data Feed -> Third Party Nio Library -> My Objects via adapter pattern
Recientemente tuvimos un problema en el que actualicé la versión de la biblioteca que estaba usando, lo que, entre otras cosas, causó que las marcas de tiempo (que la biblioteca de terceros devuelve long
) cambien de milisegundos después de la época a nanosegundos después de la época.
El problema:
Si escribo pruebas que se burlan de los objetos de la biblioteca de terceros, mi prueba será incorrecta si he cometido un error sobre los objetos de la biblioteca de terceros. Por ejemplo, no me di cuenta de que las marcas de tiempo cambiaron la precisión, lo que resultó en la necesidad de un cambio en la prueba de la unidad, porque mi simulación devolvió los datos incorrectos. Esto no es un error en la biblioteca , sucedió porque me perdí algo en la documentación.
El problema es que no puedo estar seguro acerca de los datos contenidos en estas estructuras de datos porque no puedo generar los reales sin una fuente de datos real. Estos objetos son grandes y complicados y tienen muchos datos diferentes. La documentación para la biblioteca de terceros es deficiente.
La pregunta:
¿Cómo puedo configurar mis pruebas para probar este comportamiento? No estoy seguro de poder resolver este problema en una prueba unitaria, porque la prueba en sí misma puede ser incorrecta. Además, el sistema integrado es grande y complicado y es fácil perderse algo. Por ejemplo, en la situación anterior, había ajustado correctamente el manejo de la marca de tiempo en varios lugares, pero me perdí uno de ellos. El sistema parecía estar haciendo principalmente las cosas correctas en mi prueba de integración, pero cuando lo implementé en producción (que tiene muchos más datos), el problema se hizo evidente.
No tengo un proceso para mis pruebas de integración en este momento. La prueba es esencialmente: trate de mantener las pruebas unitarias buenas, agregue más pruebas cuando las cosas se rompan, luego implemente en mi servidor de prueba y asegúrese de que las cosas parezcan sensatas, luego implemente en producción. Este problema de marca de tiempo pasó las pruebas unitarias porque los simulacros se crearon incorrectamente, luego pasó la prueba de integración porque no causó ningún problema obvio e inmediato. No tengo un departamento de control de calidad.
Timestamp
clase (que contiene cualquier representación que quieren) y proporcionar métodos llamados (.seconds()
,.milliseconds()
,.microseconds()
,.nanoseconds()
) y de constructores supuesto con nombre. Entonces no habría habido problemas.Respuestas:
Parece que ya estás haciendo la debida diligencia. Pero ...
En el nivel más práctico, siempre incluya un buen puñado de pruebas de integración de "bucle completo" en su suite para su propio código, y escriba más afirmaciones de las que cree que necesita. En particular, debe tener un puñado de pruebas que realicen un ciclo completo create-read- [do_stuff] -validate.
Y parece que ya estás haciendo este tipo de cosas. Solo se trata de una biblioteca escamosa y / o complicada. Y en ese caso, es bueno incluir algunos tipos de pruebas "así es como funciona la biblioteca" que verifican su comprensión de la biblioteca y sirven como ejemplos de cómo usar la biblioteca.
Suponga que necesita comprender y depender de cómo un analizador JSON interpreta cada "tipo" en una cadena JSON. Es útil y trivial incluir algo como esto en su suite:
Pero en segundo lugar, recuerde que las pruebas automatizadas de cualquier tipo, y en casi cualquier nivel de rigor, aún no lo protegerán contra todos los errores. Es perfectamente común agregar pruebas a medida que descubre problemas. Al no tener un departamento de control de calidad, esto significa que muchos de esos problemas serán descubiertos por los usuarios finales.
Y en un grado significativo, eso es normal.
Y en tercer lugar, cuando una biblioteca cambia el significado de un valor de retorno o campo sin renombrar el campo o método o "romper" el código dependiente (tal vez cambiando su tipo), estaría muy descontento con ese editor. Y argumentaría que, aunque probablemente debería haber leído el registro de cambios si hay uno, probablemente también deba pasar algo de su estrés al editor. Yo diría que necesitan la crítica optimista y constructiva ...
fuente
(new JSONParser()).parse(datastream)
, ya que obtienen los datos directamente de unaNetworkInterface
y todas las clases que hacen el análisis real son privadas y protegidas.NetworkInterface
... ¿es algo en lo que pueda alimentar datos conectando la interfaz a un puerto en localhost o algo así?NetworkInterface
. Es un objeto de bajo nivel para trabajar directamente con una tarjeta de red y abrir sockets, etc.Respuesta corta: es difícil. Probablemente sienta que no hay buenas respuestas, y eso es porque no hay respuestas fáciles.
Respuesta larga: como dice @ptyx , necesita pruebas del sistema y pruebas de integración, así como pruebas unitarias:
Algunas sugerencias específicas:
He visto la programación descrita como la actividad de aprender sobre un problema y un espacio de solución. Lograr que todo sea perfecto con anticipación puede no ser factible, pero puede aprender después del hecho. ("Arreglé el manejo de la marca de tiempo en varios lugares pero perdí uno. ¿Puedo cambiar mis tipos de datos o clases para hacer que el manejo de la marca de tiempo sea más explícito y más difícil de omitir, o para hacerlo más centralizado para que solo tenga un lugar para cambiar? ¿Puedo modificar? mis pruebas para verificar más aspectos del manejo de la marca de tiempo? ¿Puedo simplificar mi entorno de prueba para que esto sea más fácil en el futuro? ¿Puedo imaginar alguna herramienta que lo hubiera hecho más fácil y, de ser así, puedo encontrarla en Google? "Etc.)
fuente
Estoy totalmente en desacuerdo contigo aquí. Es un error en la biblioteca , de hecho bastante insidioso. Han cambiado el tipo semántico del valor de retorno, pero no cambiaron el tipo programático del valor de retorno. Esto puede causar todo tipo de estragos, especialmente si se trataba de una versión menor, pero incluso si se trataba de una grave.
Digamos, en cambio, que la biblioteca devolvió un tipo de
MillisecondsSinceEpoch
, un contenedor simple que contiene along
. Cuando lo cambiaron a unNanosecondsSinceEpoch
valor, su código no pudo compilarse y obviamente lo habría señalado a los lugares donde necesita realizar cambios. El cambio no pudo corromper silenciosamente su programa.Mejor aún sería un
TimeSinceEpoch
objeto que pudiera adaptar su interfaz a medida que se agregara más precisión, como agregar un#toLongNanoseconds
método junto con el#toLongMilliseconds
método, sin requerir ningún cambio en su código.El siguiente problema es que no tiene un conjunto confiable de pruebas de integración a la biblioteca. Deberías escribir esos. Mejor sería crear una interfaz alrededor de esa biblioteca para encapsularla lejos del resto de su aplicación. Varias otras respuestas abordan esto (y más siguen apareciendo mientras escribo). Las pruebas de integración deben ejecutarse con menos frecuencia que las pruebas de su unidad. Por eso es útil tener una capa de protección. Separe sus pruebas de integración en un área separada (o asígneles un nombre diferente) para que pueda ejecutarlas según sea necesario, pero no cada vez que ejecute su prueba unitaria.
fuente
...Ex()
consulte todos los métodos en Win32API). Si esto no es factible, "romper" el contrato cambiando el nombre de la función (o su tipo de retorno) hubiera sido mejor que alterar el comportamiento.Necesita pruebas de integración y sistema.
Las pruebas unitarias son excelentes para verificar que su código se comporte como espera. Como se da cuenta, no hace nada para desafiar sus suposiciones o garantizar que sus expectativas sean sensatas.
A menos que su producto tenga poca interacción con sistemas externos, o interactúe con sistemas tan conocidos, estables y documentados que puedan ser burlados con confianza (esto rara vez ocurre en el mundo real), las pruebas unitarias no son suficientes.
Cuanto más alto sea el nivel de sus pruebas, más lo protegerán contra lo inesperado. Eso tiene un costo (conveniencia, velocidad, fragilidad ...), por lo que las pruebas unitarias deben seguir siendo la base de sus pruebas, pero necesita otras capas, que incluyen, eventualmente, un poco de prueba en humanos que ayuda mucho a atrapar cosas estúpidas en las que nadie pensó.
fuente
Lo mejor sería crear un prototipo mínimo y comprender exactamente cómo funciona la biblioteca. Al hacerlo, obtendrá algunos conocimientos sobre la biblioteca con poca documentación. Un prototipo puede ser un programa minimalista que usa esa biblioteca y hace la funcionalidad.
De lo contrario, no tiene sentido escribir pruebas unitarias, con requisitos medio definidos y una comprensión débil del sistema.
En cuanto a su problema específico: sobre el uso de métricas incorrectas: lo trataría como un cambio de requisitos. Una vez que haya reconocido el problema, cambie las pruebas unitarias y el código.
fuente
Si usabas una biblioteca popular y estable, entonces podrías suponer que no te jugará trucos desagradables. Pero si cosas como lo que describiste suceden con esta biblioteca, entonces obviamente, esta no es una. Después de esta mala experiencia, cada vez que algo sale mal en su interacción con esta biblioteca, deberá examinar no solo la posibilidad de que haya cometido un error, sino también la posibilidad de que la biblioteca haya cometido un error. Entonces, digamos que esta es una biblioteca de la que no está seguro.
Una de las técnicas empleadas con las bibliotecas de las que no estamos seguros es construir una capa intermedia entre nuestro sistema y dichas bibliotecas, que abstrae la funcionalidad ofrecida por las bibliotecas, afirma que nuestras expectativas de la biblioteca son correctas y también simplifica enormemente nuestra vida en el futuro, si decidimos darle el arranque a esa biblioteca y reemplazarla con otra biblioteca que se comporte mejor.
fuente
assert
palabra clave (o función o facilidad, según el idioma que esté usando) es su amigo. No estoy hablando de aserciones en las pruebas de unidad / integración, estoy diciendo que la capa de aislamiento debe estar muy cargada de aserciones, afirmando todo lo afirmable sobre el comportamiento de la biblioteca.