Prefacio: la universidad recibi贸 una tarea: reunir un equipo scrum, seleccionar un proyecto y trabajar en 茅l durante un semestre. Nuestro equipo eligi贸 el desarrollo de aplicaciones web (reaccionar + matraz). En este art铆culo tratar茅 de decirle qu茅 pruebas deber铆an haber sido y analizar lo que hicimos en el backend.

Las expectativas
Las pruebas son necesarias, en primer lugar, para convencer a todos (incluidos nosotros mismos) de que el programa se comporta como deber铆a en situaciones de prueba . En segundo lugar, aseguran el rendimiento del c贸digo cubierto por las pruebas en el futuro. Escribir pruebas es un proceso 煤til, porque en su proceso a menudo puede tropezar con 谩reas problem谩ticas, recordar algunos casos extremos, ver problemas con las interfaces, etc.
Al desarrollar cualquier sistema, debe recordar al menos tres tipos de pruebas:
- Las pruebas unitarias son pruebas que verifican que las funciones hacen lo que necesitan.
- Las pruebas de integraci贸n son pruebas que verifican que varias funciones juntas hacen lo correcto.
- Las pruebas del sistema son pruebas que verifican que todo el sistema hace lo que necesita.
En una de las publicaciones de google, se public贸 una tabla con una descripci贸n de tres tipos de pruebas. "Peque帽o", "Medio" y "Grande".

Pruebas unitarias
Las pruebas unitarias corresponden a pruebas peque帽as: deben ser r谩pidas y solo verificar la correcci贸n de partes espec铆ficas del programa. No deber铆an acceder a la base de datos, no deber铆an funcionar en entornos complejos de subprocesos m煤ltiples. Controlan el cumplimiento de las especificaciones / est谩ndares, a menudo tienen el papel de pruebas de regresi贸n .
Pruebas de integraci贸n
Las pruebas de integraci贸n son aquellas que pueden afectar varios m贸dulos y funciones. Dichas pruebas requieren m谩s tiempo y pueden requerir entornos especiales. Son necesarios para asegurarse de que los m贸dulos y las funciones individuales puedan funcionar entre s铆. Es decir las pruebas unitarias verifican la conformidad de las interfaces reales con las esperadas, y las pruebas de integraci贸n, que las funciones y los m贸dulos interact煤an correctamente entre s铆.
Pruebas del sistema
Este es el nivel m谩s alto de pruebas autom谩ticas. Las pruebas del sistema verifican que todo el sistema funciona, que sus partes realizan sus tareas y pueden interactuar correctamente.
驴Por qu茅 hacer un seguimiento de los tipos?
Por lo general, con el crecimiento del proyecto, la base del c贸digo tambi茅n crecer谩. La duraci贸n de las verificaciones autom谩ticas aumentar谩; admitir una gran cantidad de pruebas de integraci贸n y del sistema ser谩 cada vez m谩s dif铆cil. Por lo tanto, el desaf铆o para los desarrolladores es minimizar las pruebas necesarias. Para hacer esto, intente usar pruebas unitarias cuando sea posible y reduzca la integraci贸n usando "simulacros" (simulacros).
Realidad
Prueba t铆pica de API
def test_user_reg(client): return json.loads( client.post(url, json=data, content_type='application/json').data ) response = client.post('api/user.reg', json={ 'email': 'name@mail.ru', 'password': 'password1', 'first_name': 'Name', 'last_name': 'Last Name' }) data = json.loads(response.data) assert data['code'] == 0
De la documentaci贸n oficial del matraz, obtenemos una receta preparada para inicializar la aplicaci贸n y crear la base de datos. Aqu铆 est谩 el trabajo con la base de datos. Esta no es una prueba unitaria, pero no es una prueba del sistema. Esta es una prueba de integraci贸n que utiliza una aplicaci贸n de prueba de base de datos.
驴Por qu茅 integraci贸n en lugar de modular? Porque en el procesamiento de consultas, la interacci贸n se realiza con el matraz, con ORM, con nuestra l贸gica de negocios. Los controladores act煤an como un enlace unificador de otras partes del proyecto, por lo que escribir pruebas unitarias para ellos no es demasiado f谩cil (necesita reemplazar la base de datos con simulacros, l贸gica interna) y no es demasiado pr谩ctico (las pruebas de integraci贸n verifican aspectos similares: "驴se llamaron las funciones necesarias?", " 驴Se recibieron correctamente los datos? ", Etc.).
Nombres y agrupaci贸n de pruebas.
def test_not_empty_errors(): assert validate_not_empty('email', '') == ('email is empty',) assert validate_not_empty('email', ' ') == ('email is empty',) assert validate_email_format('email', "") == ('email is empty',) assert validate_password_format('pass', "") == ('pass is empty',) assert validate_datetime('datetime', "") == ('datetime is empty',)
En esta prueba, se cumplen todas las condiciones para las pruebas "peque帽as": se verifica que el comportamiento de la funci贸n sin dependencias cumpla con lo esperado. Pero el dise帽o plantea preguntas.
Es una buena pr谩ctica escribir pruebas que se centren en un aspecto espec铆fico del programa. En este ejemplo, hay diferentes funciones: validate_password_format
, validate_password_format
, validate_datetime
. Las comprobaciones de agrupamiento no se basan en el resultado, sino en los objetos de prueba.
El nombre de la prueba ( test_not_empty_errors
) no describe el objeto de prueba (qu茅 m茅todo se est谩 probando), solo describe el resultado (los errores no est谩n vac铆os). Este m茅todo debe llamarse test__validate_not_empty__error_on_empty
. Este nombre describe lo que se est谩 probando y qu茅 resultado se espera. Esto se aplica a casi todos los nombres de prueba en el proyecto debido al hecho de que no se tom贸 tiempo para discutir las convenciones de nombres de prueba.
Pruebas de regresi贸n
def test_datetime_errors(): assert validate_datetime('datetime', '0123-24-31T;431') == ('datetime is invalid',) assert validate_datetime('datetime', '2018-10-18T20:21:21+-23:1') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-13-20T20:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-02-29T20:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T25:20:20+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:61:20+22:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:61+20:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:20+25:20') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-12-20T20:20:20+20:61') == ('datetime is invalid',) assert validate_datetime('datetime', '2015-13-35T25:61:61+61:61') == ('datetime is invalid',)
Esta prueba originalmente consisti贸 en las dos primeras assert
. Despu茅s de eso, se descubri贸 un "error": en lugar de verificar la fecha, solo se verific贸 la expresi贸n regular, es decir 9999-99-99
se consider贸 una fecha normal. El desarrollador lo arregl贸. Naturalmente, despu茅s de corregir el error, debe agregar pruebas para evitar una regresi贸n futura. En lugar de agregar una nueva prueba en la que escribir por qu茅 existe esta prueba, se agregaron cheques a esta prueba.
驴C贸mo deber铆a llamarse una nueva prueba para agregar la verificaci贸n? Probablemente test__validate_datetime__error_on_bad_datetime
.
Ignorando las herramientas
def test_get_providers(): class Tmp: def __init__(self, id_external, token, username): self.id_external = id_external self.token = token self.username = username ...
Tmp
? Esta es una sustituci贸n de un objeto que no se usa en esta prueba. El desarrollador no parece saber sobre la existencia de @patch
y MagicMock
de unittest.mock
. No es necesario complicar el c贸digo resolviendo problemas ingenuamente cuando hay herramientas m谩s adecuadas.
Existe una prueba que inicializa los servicios (en la base de datos) y utiliza el contexto de la aplicaci贸n.
def test_get_posts(client): def fake_request(*args, **kwargs): return [one, two] handler = VKServiceHandler() handler.request = fake_request services_init() with app.app_context(): posts = handler.get_posts(None) assert len(posts) == 2
Puede excluir la base de datos y el contexto de la prueba simplemente agregando un @patch
.
@patch("mobius.services.service_vk.Service") def test_get_posts(mock): def fake_request(*args, **kwargs): return [one, two] handler = VKServiceHandler() handler.request = fake_request posts = handler.get_posts(None) assert len(posts) == 2
Resumen
- Para desarrollar software de calidad, debe escribir pruebas. Como m铆nimo, para asegurarse de escribir lo que necesita.
- Para sistemas de informaci贸n grandes, las pruebas son a煤n m谩s importantes: le permiten evitar cambios de interfaz no deseados o errores de retorno.
- Para que las pruebas escritas no se conviertan en muchos m茅todos extra帽os con el tiempo, debe prestar atenci贸n a la convenci贸n de nomenclatura de las pruebas, cumplir con las buenas pr谩cticas y minimizar las pruebas.
- Las pruebas unitarias pueden ser una gran herramienta durante el desarrollo. Se pueden ejecutar despu茅s de cada peque帽o cambio para asegurarse de que no se rompa nada.
Un punto muy importante es que las pruebas no garantizan la disponibilidad o ausencia de errores. Las pruebas aseguran que se espera el resultado real del programa (o parte de 茅l). En este caso, la verificaci贸n solo ocurre para aquellos aspectos para los cuales se escribieron las pruebas. Por lo tanto, al crear un producto de calidad, no debemos olvidarnos de otros tipos de pruebas.