Está en la página 1de 74

Módulo 3.

UniTest

Las pruebas unitarias o unit testing son una forma de comprobar que un fragmento
de código funciona correctamente. Es uno de los procedimientos que se llevan a
cabo dentro de una metodología ágil de trabajo.

Video de inmersión

Unidad 1: Test automático

Unidad 2. Pruebas unitarias

Glosario

Video de habilidades

Referencias
Lección 1 de 6

Video de inmersión

Verify to continue
We detected a high number of errors from your
connection. To continue, please confirm that
you’re a human (and not a spambot).

I'm not a robot


reCAPTCHA
Privacy - Terms

C O NT I NU A R
Lección 2 de 6

Unidad 1: Test automático

Tema 1. ¿Qué es y para qué sirve un test automatizado?

Cuando hablamos de programas grandes donde intervienen varios desarrolladores que trabajan
simultáneamente, el proceso de pruebas cobra especial importancia si consideramos que:

Cuando una sola persona al hacer un programa más o menos pequeño, normalmente,
puede ir modificando su código poco a poco, compilar de vez en cuando, ejecutarlo y
ver si lo que está haciendo funciona. El proceso de probar a mano puede llevarle un
tiempo razonable y el programa puede, al final, quedar perfectamente hecho.

Al ser un programa grande, las pruebas manuales deben ser frecuentes, para comprobar
que según pasa el tiempo, lo que estaba funcionando, sigue funcionando, además de
comprobar que lo que acabamos de hacer también funciona. Pero al mismo tiempo, al
ser el programa más grande, esas pruebas manuales llevan cada vez más tiempo, ya
que cada vez hay más cosas que probar.

Sería maravilloso si hubiera algo que probara nuestro programa de forma automática y
con frecuencia. Si cada vez que hacemos cambios o añadimos código le decimos a
ese algo que prueba nuestro programa y nos despreocupamos del tema, nos
ahorraríamos mucho tiempo de prueba.
Afortunadamente, existe ese algo. Podemos hacer un programa que se encargue de
probar nuestro programa. Ese programa de pruebas debería ser fácil de lanzar,
deberíamos lanzarlo con frecuencia, no debería requerir ninguna intervención por
nuestra parte y no debería darnos unos resultados que tengamos que analizar.
Únicamente debemos lanzarlo y él solo debe decirnos: "Todas las pruebas han ido
bien". O, si no hemos tenido suerte (si tal cosa existe): "Han fallado estas pruebas en
estos sitios".

Obviamente, hacer estos programas de prueba es más trabajo, pero cuando más
grande y complejo es el proyecto en el que estamos involucrados, más tiempo vamos a
ahorrar a la larga. Las ventajas de tener unos buenos programas de prueba son:

1. Al ser la prueba automática, nos dará menos pereza lanzarla con frecuencia. De hecho,
lo ideal es hacer un trozo de código y pasar las pruebas. Si hemos fastidiado algo,
sabremos que el trozo de código que acabamos de tocar es el que ha introducido el
fallo. Si la prueba es larga y manual, seguramente probaremos con menos frecuencia.
Si algo se estropea, quizás tengamos que buscar el fallo en el código que hemos hecho
durante toda la semana.

2. Tener las pruebas automáticas nos quita el miedo a tocar el código que ya está
funcionando. A veces vemos código que está funcionando, pero que no está todo lo
bien hecho que debiera. Otras veces, tenemos que arreglar un poco ese código para
poder reutilizarlo en una nueva funcionalidad que queremos dar a nuestro.

3. Si no tenemos una forma fácil de ver que ese código sigue funcionando después de
tocarlo, igual no nos decidimos a tocarlo.

4. Una prueba automática que falla nos da más información que una prueba manual que
detecta un fallo. Si una prueba automática falla, nos dice exactamente qué método del
código no hace lo que se esperaba de él. Si una prueba manual encuentra un fallo, solo
sabemos que algo en cualquier parte del código no está bien.

Las pruebas automáticas ayudan mucho, pero tampoco garantizan nada al 100%.
Nunca nos quitaremos del todo la necesidad de hacer pruebas manuales, pero incluso
haciendo pruebas manuales, algunos errores colapsarán y saldrán más adelante... o
nunca. Las pruebas automáticas son simplemente una primera criba que nos ahorrará
tiempo al detectar automáticamente un porcentaje alto de los errores en el código.

Actualmente, en cualquier proyecto de software mínimamente serio, es impensable no


realizar test automáticos de prueba, ya que el ahorro de tiempo y la mejora
del software al hacer estos test está de sobra probada. (Chuidiang.com, s. f.,
https://n9.cl/azgy3).

Resumiendo, los test (pruebas) automáticos son programas que se encargan de probar nuestro
programa. Estos programas de pruebas deberían tener la particularidad de ser fácil de lanzar. Además,
deberían prescindir de la intervención del desarrollador y darnos resultados que no requieran de análisis
posterior.

Nos sirven para evitar que se comentan errores en el código, ya que nos indicarán en qué funcionalidad
no pasó la prueba. Nos dan más información al detectar un fallo. Nos permiten generar una frecuencia
constante en las pruebas, lo que nos da más confianza en el desarrollo.

Tema 2. Arquitecturas de pruebas automatizadas

La automatización de la prueba de software (conjunto de programas) es un tipo particular de sistema de


software, a veces llamado framework (entorno de trabajo) de prueba o stack de prueba. Como cualquier
sistema de software, tiene una arquitectura que puede facilitar o no la extensión.

Es la que proporciona una visión arquitectónica completa del sistema de automatización de pruebas,
mediante una serie de vistas arquitectónicas distintas para representar diferentes aspectos del sistema.

Sirve como medio para gestionar y comunicar las características fundamentales y las funciones del
sistema de software de automatización de pruebas. Ofrece una perspectiva dominante para el software
de automatización de pruebas que habilita la ejecución del sistema necesario respecto a ciertos
aspectos claves: mantenimiento, capacidad de ampliación, fiabilidad, concurrencia, distribución,
seguridad y recuperación.
Figura 1. UI test architecture

Fuente: Microsoft, 2019, https://n9.cl/twcq7

Las tareas de automatización de pruebas están cada vez más presentes en el mercado. Hay disponibles
gran cantidad de herramientas de automatización. Sin embargo, es poco común que una única
herramienta sea capaz de automatizar todas las tareas de prueba.

En general, la gran mayoría de estas herramientas se enfocan en una tarea o en un grupo de tareas
específicas. Hay otras, en cambio, que se encargan de un aspecto específico de una tarea.

Tipos de pruebas de software


Básicamente, todos los tipos de pruebas de software que existen se pueden agrupar en dos tipos: las
pruebas funcionales y las pruebas no funcionales.

Pruebas funcionales

Las pruebas funcionales se definen teniendo como fuente los requisitos del sistema,
estas pruebas validan y verifican que el producto cumple con lo especificado y hace lo
que debe y cómo lo tiene que hacer dando también una idea del grado de calidad del
software. (Tester House, 2019).

Dentro de las pruebas funcionales tenemos:

Pruebas unitarias.

Pruebas de aceptación.

Pruebas de integración.

Pruebas de regresión.

Pruebas de humo.

Pruebas de cordura.

Pruebas de componentes.

Pruebas unitarias

Son las que aseguran que cada célula del código desarrollado en un componente brinde los resultados
adecuados. En estas pruebas los desarrolladores observan la interfaz y la especificación de un
componente, proporcionando la documentación del desarrollo del código.

Las pruebas unitarias admiten pruebas funcionales al ejercer el código que es más probable que se
rompa. Por ello, si usamos pruebas funcionales sin pruebas unitarias, podemos experimentar algunas
dificultades para diagnosticar pruebas fallidas. Así que hay que tenerlas muy presentes.
Pruebas de componentes

Las pruebas de componentes se ejecutan de forma independiente para comprobar que el resultado sea
el requerido. Su objetivo es verificar las funcionalidades o usabilidades de los componentes, aunque no
solo se limite a eso.

Para ilustrar mejor, un ejemplo de esta prueba puede ser cualquier elemento que tenga entrada y deba
generar alguna salida. Puede ser el módulo de código, página web, pantallas e incluso un sistema
dentro de un sistema más grande. Aquí, algunos usos de los componentes que podemos probar:

Prueba de UI para usabilidad y accesibilidad.

Prueba de carga para asegurar el rendimiento.

Inyección de SQL a través de componentes de UI para asegurar la seguridad.

Prueba de login con credenciales válidas e inválidas.

Pruebas de aceptación del usuario

Cuando ya hemos seguido e implementado las pruebas que requerimos para nuestro
producto, hacemos las pruebas de aceptación. Estas hacen parte de la última fase de
este proceso de testing. Aquí los usuarios reales del software lo usan para verificar
que cumpla con las tareas requeridas en un ambiente “real”. En ocasiones se realiza
cuando se hace la entrega del producto como punto de control final entre todos los
tipos de pruebas funcionales.

Desde el inicio hasta la implementación, el software deberá someterse a varios tipos de


pruebas. El objetivo siempre será asegurar la calidad para evitar reprocesos y garantizar
las funcionalidades de la aplicación, tanto para el usuario final, como para el cliente.

Pruebas de integración
Es uno de los tipos de prueba funcional más común y se realiza de forma
automatizada. Se realizan para probar componentes individuales con el objetivo de
verificar cómo los módulos, que trabajan de forma individual, funcionan cuando están
integrados.

El objetivo de realizar estas pruebas es porque comúnmente los desarrolladores se


enfocan en construir diferentes módulos del sistema simultáneamente y no se centran
en otros. Las pruebas de integración permiten que los datos y comandos operativos
fluyan entre módulos. Hacer que todo actúe como partes de un solo sistema en lugar de
aplicativos aislados.

Usualmente nos ayuda a identificar problemas en las operaciones de la interfaz de


usuario, formatos de datos, invocar API, acceso a bases datos, entre otras.

Una de las verificaciones que se realizan en las pruebas de integración es la Prueba de


interfaz: en la comprobación de las transferencias de datos entre dos componentes.
Prueba de interfaces como servicios web, API, entre otros. Se realiza para verificar que
los componentes estén sincronizados entre sí. Ayudan a determinar que diferentes
funciones, como la transferencia de datos entre los diferentes elementos del sistema,
se realizan de acuerdo con la forma en que fueron diseñadas.

Pruebas de regresión

Es normal que los desarrolladores modifiquen y mejoren las funcionalidades de su


desarrollo. Por ello existe una gran posibilidad de que puedan causar ‘efectos’
inesperados en su comportamiento. Estas pruebas de regresión se realizan para
asegurar que los cambios o adiciones no hayan alterado ni eliminado las
funcionalidades existentes.

El objetivo de las pruebas de regresión es encontrar errores que puedan haber sido
introducidos accidentalmente en la compilación existente y así garantizar que los
errores eliminados continúen así.
Pruebas de humo

Se realizan para verificar si las funcionalidades más significativas de la aplicación


funcionan o no. De forma que lo más básico del software se ejecute de forma correcta
con pruebas sencillas y rápidas.

Es una de las pruebas funcionales más importantes y debería ser la primera que se
realice en una nueva compilación. La prueba de humo es común y aunque a veces no
se tiene claro su concepto. No se trata de realizar pruebas exhaustivas, sino de
verificar que la funcionalidad crítica del sistema realmente funciona bien.

Si la prueba es exitosa será entonces una compilación estable. El equipo realizará


pruebas funcionales para las características o funcionalidades recién agregadas
posteriormente o pruebas de regresión según la situación. Por otro lado, si esta no es
estable y falla la compilación lo común es que se devuelva al equipo de desarrollo para
solucionar los problemas de compilación y crear una nueva.

Prueba de cordura

Si tienes una compilación con modificaciones menores, en vez de ejecutar las pruebas
de regresión, realizamos este tipo de prueba.

Con ella podemos determinar que las modificaciones realmente hayan solucionado los
problemas. Y que dichas correcciones no hayan generado ningún problema. Usualmente
estas pruebas son subpruebas de la de “regresión”, ya que están relacionadas con los
cambios realizados en el producto. No confundas las pruebas de humo con las de
cordura por una simple razón.

Las pruebas de humo se inician en la compilación desde el inicio y se inspeccionan las


funcionalidades más importantes. Mientras que las de cordura analizan profundamente
las compilaciones de software. Es decir, las primeras confirman la estabilidad del
producto, mientras que las segundas aseguran la racionalidad del producto. (Vargas, s.
f., https://n9.cl/03cs2).
Pruebas no funcionales

Las pruebas de software no funcionales son las que se hacen desde una perspectiva
totalmente diferente a las pruebas automatizadas. Este tipo de plan de pruebas son un
medio de control de calidad, que se realiza en aplicaciones de software para asegurarse
de que todo funciona bien y poder saber en qué circunstancias podrían fallar.

Las pruebas no funcionales de software nos permiten conocer qué riesgos corre
el producto y nos dicen si tiene un mal desempeño o un bajo rendimiento en los
entornos de producción. En ese sentido, las pruebas de software no funcionales se
hacen con el fin de obtener información. Permiten explicar lo que soporta el producto y
si cumple con las expectativas de los clientes. Las pruebas no funcionales son:

Pruebas de carga.

Pruebas de estrés.

Pruebas de rendimiento.

Pruebas de carga

Estas pruebas se hacen con el objetivo de determinar y validar la respuesta de la


aplicación cuando esta está sometida a una carga de un cierto número de usuarios o de
peticiones.

Ejemplo: Verificar si el producto puede soportar la carga de 100 usuarios de forma


simultánea. Este resultado se compara con el volumen esperado.

Pruebas de rendimiento

El principal objetivo de este tipo de pruebas no funcionales es calcular la respuesta de


la aplicación con diferentes medidas de usuario o peticiones.
Ejemplo: conocer cuál es la respuesta al procesar el ingreso de 10, 100 y 1000 usuarios
de forma parametrizada. Este resultado se compara con el resultado esperado.

Pruebas de estrés

Estas pruebas se realizan para encontrar el número de usuarios, peticiones o tiempos


que la aplicación puede soportar. Este tipo de pruebas no funcionales son muy
semejantes a las pruebas de carga y rendimiento, pero se diferencian en que debemos
superar los límites esperados en el ambiente de producción o los límites que fueron
determinados en las pruebas.

Ejemplo: encontrar la cantidad de usuarios que soporta de manera simultánea hasta que
la aplicación deja de responder (cuelgue o time out), haciéndolo de forma correcta
según todas las peticiones. (Soto Morales, 2021, https://n9.cl/vm3kz).

También hay que tener en cuenta las llamadas pruebas de caja blanca y pruebas de caja negra. En
realidad, estas no son tipos de pruebas, sino que se definen como técnicas de pruebas de software.

Técnicas de pruebas de software

Al evaluar las diferentes herramientas para la automatización de pruebas, es importante conocer el tipo
de técnica que compone la herramienta. En ese sentido, hay que conocer sus limitaciones y el tipo de
tareas a las que se dirige y que puede automatizar. Las herramientas de prueba suelen evaluarse y
adquirirse en función de:

Función.

Caja blanca en oposición a caja negra.


Especialización.

Función

Las herramientas de prueba se pueden categorizar según las funciones que realicen.
Algunas designaciones de funciones típicas para herramientas son:

Herramientas de adquisición de datos que adquieren datos para utilizar en las tareas
de prueba. Los datos se pueden adquirir mediante la conversión, la extracción, la
transformación o la captura de datos existentes, o mediante la generación de guiones
de uso o especificaciones suplementarias.

Herramientas estáticas de medida que analizan información contenida en los modelos


de diseño, el código fuente u otros orígenes fijos. El análisis produce información en el
flujo lógico, el flujo de datos o la métrica de calidad, como la complejidad, el
mantenimiento o las líneas de código.

Herramientas dinámicas de medida que realizan un análisis durante la ejecución del


código. Las medidas incluyen la operación de tiempo de ejecución del código, como la
memoria, la detección de errores y el rendimiento.

Simuladores o controladores que realizan tareas que, por cuestiones de tiempo,


gastos o seguridad no están disponibles para las pruebas.

Herramientas de gestión de pruebas que ayudan en la planificación, el diseño, la


implementación, la ejecución, la evaluación y la gestión de tareas de prueba o
productos de trabajo.

Caja blanca en oposición a caja negra

Las herramientas de prueba suelen caracterizarse como cajas blancas o cajas negras
en función de cómo se utilicen, o la tecnología y los conocimientos necesarios para
utilizarlas.

Las herramientas de caja blanca dependen del conocimiento del código, los modelos
de diseño y otro material de origen para implementar y ejecutar las pruebas.
Las herramientas de caja negra dependen de los guiones de uso o la descripción
funcional del destino de la prueba.

Las herramientas de caja blanca saben cómo procesa la solicitud el destino de la


prueba, mientras que las herramientas de caja negra dependen de las condiciones de
entrada y de salida para evaluar la prueba.

Especialización

Además de las amplias clasificaciones de herramientas que se presentaron antes, las


herramientas también se pueden clasificar según la especialización.

Las herramientas de grabación y reproducción combinan la adquisición de datos con


la medida dinámica. Los datos de prueba se adquieren durante la grabación de sucesos
(conocida como implementación de la prueba). Más tarde, durante la ejecución de la
prueba, los datos se utilizan para reproducir el script de prueba, que se utiliza para
evaluar la ejecución del destino de la prueba.

Las herramientas de métrica de calidad son herramientas de medida estática que


realizan un análisis estático de los modelos de diseño o código fuente para establecer
un conjunto de parámetros que describen la calidad del destino de la prueba. Los
parámetros pueden indicar fiabilidad, complejidad, mantenimiento u otras medidas de
calidad.

Las herramientas de supervisión de la cobertura indican la completitud de la prueba


mediante la identificación de la cantidad de destino de la prueba cubierta, en alguna
dimensión, durante la prueba. Las clases típicas de cobertura son guiones de uso
(basados en requisitos), nodo o ramificación lógica (basada en código), estado de los
datos y puntos de función.

Los generadores de guiones de prueba automatizan la generación de los datos de


prueba. Los generadores de guiones de prueba utilizan o bien una especificación formal
de las entradas de datos del destino de la prueba o bien los modelos de diseño y el
código fuente para producir datos de prueba que prueben las entradas nominales, las
entradas de errores y los guiones de límite.

Las herramientas del comparador comparan los resultados de la prueba con los
resultados de referencia e identifican las diferencias. Los comparadores se diferencian
en su especificación para formatos de datos particulares. Por ejemplo, pueden basarse
en píxeles para comparar imágenes de mapa de bits o en objetos para comparar las
propiedades o los datos del objeto.

Los extractores de datos proporcionan entradas para los guiones de prueba de


orígenes existentes, incluidos bases de datos, secuencias de datos de un sistema de
comunicación, informes o modelos de diseño y código fuente. (IBM Corp., 2006,
https://n9.cl/dywlb).

Tema 3. Beneficios del testing

El testing (proceso de prueba) es una poderosa estrategia para bajar los costos y optimizar los tiempos.

Los beneficios de implementar esta práctica en un proyecto, causan un gran impacto


desde el inicio hasta el fin, influyendo tanto en los procesos de trabajo como en los
resultados enfocados al negocio. En lo que respecta a este tema vamos a describir los
seis principales motivos por los cuales conviene adoptar el testing automatizado.

Las razones más destacadas serían:

RAPIDEZ. Al automatizar, se reduce en un 90 % el costo con relación al testing


manual. El conjunto de los casos automatizados es utilizado para ejecutar pruebas de
regresión, lo cual permite reducir el tiempo que demanda el testing manual a un 10%.
Es decir, que si para efectuar pruebas de regresión antes requería un mes (30 días) de
un tester manual, con los test automatizados se invierten solamente 3 días de una PC
o 1 día de 3 PC y sin intervención humana, sin errores humanos, y con la consecuente
reducción de costos e incremento de su productividad.

Es decir, las pruebas automatizadas multiplican la capacidad de los equipos de


testing o unidades de calidad de las organizaciones. Minimiza sustancialmente la
ejecución de las pruebas manuales, refiriendo estas últimas solo en escenarios
especiales. De este modo el recurso humano puede realizar otro tipo de tareas,
mientras la automatización se ocupa de realizar las pruebas relevantes y repetitivas.

Por otra parte, permite ejecutar mayor cantidad de pruebas, sin necesidad de
incrementar el número de personas dedicadas a testear. Esto hace que el equipo de
trabajo sea mucho más eficiente y productivo, haciendo rentable la ejecución de
pruebas de software.
VERSATILIDAD. Da solución a un problema que sufren muchas empresas que cuentan
con sistemas “Legacy” (heredados, antiguos), para los cuales es tan difícil encontrar
técnicos especializados para su mantenimiento como técnicos que puedan ocupar su
tiempo y conocimiento en probar las modificaciones que se implementan.

SIMPLIFICACIÓN. No es necesario conocer lenguajes complejos para la codificación, a


diferencia de otras herramientas para las cuales es obligatorio el conocimiento de algún
lenguaje de programación o que solamente pueden automatizar el test de aplicaciones
web.

ASERTIVIDAD. Ayuda a los testers a validar que el objetivo de la prueba ha sido


alcanzado apropiadamente y se ha correspondido con el resultado esperado y
especificado previamente.

FLEXIBILIDAD. Es posible integrar las herramientas de automatización del testing con


otras que ya se encuentren siendo utilizadas para realizar la misma tarea sin necesidad
de desechar el trabajo previamente realizado, pudiendo agregar las características
antes mencionadas para mejorar la eficiencia y los resultados perseguidos.

ANÁLISIS. Las corridas programadas de los test automatizados generan reportes que
permiten conocer el tiempo completo de la ejecución de la totalidad de los casos, el
tiempo de ejecución para cada caso, el resultado obtenido luego de cada prueba y, de
producirse, el error que haya provocado la falla de alguna de las pruebas, lo que
permitirá a posteriori modificar el código necesario para que el sistema continúe su
funcionamiento tal cual lo esperado. (Lezcano, 2020, https://n9.cl/txtwo).

Mayor capacidad de ejecución de pruebas.

Es posible realizar un gran número de pruebas en un breve período de tiempo. Las


mismas pueden ser ejecutadas durante las 24 horas, los 7 días de la semana, incluso
cuando el equipo de test no esté en la oficina. Se pueden ejecutar en forma paralela y
sobre distintos servidores, aumentando aún más su capacidad.

Integración continua y Devops.

Devops es una importante tendencia en la construcción de software moderno. Tiene


como objetivo agilizar el proceso de liberación de software, buscando responder
rápidamente a las exigencias de los negocios. En ese escenario, que las pruebas estén
automatizadas, resulta vital para acelerar todo el proceso de entrega. Esto permite, en
forma temprana, detener el proceso de salida a producción si algo no está bien, Las
pruebas automatizadas permiten detectar fallas de regresión, de integración,
funcionales, entre otras. De esta forma es posible asegurar que el software cumpla con
los estándares de calidad establecidos entregando versiones confiables.

Pruebas repetibles.

El trabajo que implica desarrollar una prueba automatizada es recompensado por la gran
cantidad de veces que será ejecutada. En los casos donde la prueba requiere de poco
mantenimiento, porque el software es estable, el beneficio es la practicidad, ya que la
prueba se realiza sin esfuerzo alguno. Para test regresivos, las pruebas automatizadas
se podrán ejecutar una y otra vez a medida que el software evolucione, asegurando
consistencia y que todo lo que funcionaba en la versión anterior, seguirá funcionando en
la nueva. (Software Testing Bureau, 2021, https://n9.cl/6pxw).

En conclusión, el testing automatizado es una práctica que hace viable la producción de una solución
con pruebas eficientes. Además, permite concentrar los esfuerzos del equipo de testing en las pruebas
más críticas, haciendo un excelente complemento con las pruebas automatizadas.

Tema 4. Ejemplos prácticos de herramientas para


automatizar los testing

Una revisión efectiva del código evita que los errores y fallos se introduzcan en el proyecto, mejorando la
calidad del código en una fase temprana del proceso de desarrollo del software.

El objetivo principal del proceso de revisión del código es evaluar cualquier nuevo
código para detectar errores, fallos y normas de calidad establecidas por la
organización. El proceso de revisión del código no debe consistir solo en una
retroalimentación unilateral. Por lo tanto, un beneficio intangible del proceso de revisión
de códigos es la mejora de las aptitudes de codificación del equipo colectivo.

Si deseas iniciar un proceso de revisión del código en la organización donde prestas


servicios, primero debes decidir quién revisará el código. Si perteneces a un equipo
pequeño, puedes asignar a los líderes del equipo para que revisen todo el código. En un
equipo más grande con múltiples revisores, podrías habilitar un proceso en el que cada
revisión de código se asigne a un desarrollador experimentado en función de su carga
de trabajo. (Daityari, 2020, https://n9.cl/403aa).

A continuación, revisaremos las herramientas de revisión de código estático más populares.

1 Review Board.

2 Crucible.

3 GitHub.

4 Phabricator.

5 Collaborator.

6 CodeScene.

7 Visual Expert.

8 Gerrit.

9 Rhodecode.

Veracode.
10
11 Reviewable.

12 Peer Review for Trac.

Figura 2. Review Board

Fuente: Daityari, 2020, https://n9.cl/403aa

Es una herramienta de código abierto basada en la web para la revisión de códigos.


Para probar esta herramienta de revisión de código, puedes explorar la demostración en
tu sitio web o descargar y configurar el software en tu servidor.

El lenguaje de programación Python y sus instaladores, MySQL o PostgreSQL como


base de datos, y un servidor web son los requisitos previos para ejecutar Review Board
en un servidor.
Puedes integrar el Review Board con una amplia gama de sistemas de control de
versiones: Git, Mercurial, CVS, Subversion y Perforce. También puedes vincular el
Review Board a Amazon S3 para almacenar capturas de pantalla directamente en la
herramienta. (Daityari, 2020, https://n9.cl/403aa).

Figura 3. Review Board

Fuente: Daityari, 2020, https://n9.cl/403aa

Review Board te permite realizar revisiones de código antes y después de la


autorización, dependiendo de tus necesidades. Si no has integrado un sistema de
control de versiones, puedes utilizar un archivo de diferencias para cargar los cambios
de código en la herramienta para una revisión.

También se proporciona una comparación gráfica de los cambios en su código. Además


de las revisiones del código, Review Board te permite realizar revisiones de
documentos también.

La primera versión de Review Board salió hace más de una década, pero todavía está
en desarrollo activo. Por lo tanto, la comunidad de Review Board ha crecido a lo largo
de los años y es probable que encuentre soporte si tiene algún problema al usar la
herramienta.

El Review Board es una simple herramienta para revisiones de código, que puedes
alojar en tu servidor. Deberías probarla si no deseas alojar tu código en un sitio web
público. (Daityari, 2020, https://n9.cl/403aa).

Figura 4. Crucible

Fuente: Daityari, 2020, https://n9.cl/403aa

Es una herramienta de revisión de código en colaboración de Atlassian. Es un conjunto


de herramientas comerciales que permite revisar el código, discutir cambios en los
planes e identificar errores en una serie de sistemas de control de versiones.
Crucible, es una herramienta paga.

Similarmente al Review Board, Crucible soporta un gran número de sistemas de control


de versiones: SVN, Git, Mercurial, CVS y Perforce. Su función principal es permitirte
realizar revisiones de código. Además de los comentarios generales sobre el código, te
permite hacer comentarios en línea dentro de la vista de diferencias para señalar
exactamente a qué te refieres específicamente.

Crucible se integra bien con otros productos empresariales de Atlassian como


Confluence y Enterprise BitBucket. Sin embargo, es posible que obtengas los mayores
beneficios de Crucible al usarlo junto con Jira, Atlassian’s Issue y Project Tracker. Te
permite realizar revisiones y auditorías previas al compromiso en el código fusionado.
(Daityari, 2020, https://n9.cl/403aa).

Figura 5. GitHub

Fuente: Daityari, 2020, https://n9.cl/403aa


GitHub tiene una herramienta de revisión de código incorporada en sus solicitudes de
extracción. La herramienta de revisión de código está incluida en el servicio principal de
GitHub, que proporciona un plan gratuito para los desarrolladores. El plan gratuito de
GitHub limita el número de usuarios a tres en los repositorios privados. GitHub, es una
combinación, ya que ofrece ambas modalidades, tiene un módulo que puede ser
gratuito, y otros módulos pagos, que incluyen funcionalidades que no están en la
Gratuita.

GitHub permite a un revisor con acceso al repositorio de códigos asignarse a la solicitud


de extracción y completar una revisión. Un desarrollador que ha enviado la petición pull
(petición de empuje) también puede solicitar una revisión de un administrador.

Además de la discusión sobre la solicitud de extracción general, puedes analizar el


diferencial, comentar en línea y comprobar el historial de cambios. La herramienta de
revisión de código también te permite resolver conflictos simples de Git a través de la
interfaz web. GitHub incluso te permite integrarte con herramientas de revisión
adicionales a través de su mercado para crear un proceso más robusto.

La herramienta de revisión de código de GitHub es una gran herramienta si ya estás en


la plataforma. No requiere ninguna instalación o configuración adicional. El principal
problema de la herramienta de revisión de código de GitHub es que solo admite los
repositorios de Git alojados en GitHub. Si buscas una herramienta de revisión de código
similar que puedas descargar y alojar en tu servidor, puedes probar GitLab. (Daityari,
2020, https://n9.cl/403aa).

Figura 6. Phabricator
Fuente: Daityari, 2020, https://n9.cl/403aa

Es una lista de herramientas de código abierto de Phacility que te ayudan a revisar el


código. Mientras que puedes descargar e instalar el conjunto de herramientas de
revisión de código en tu servidor, Phacility también proporciona una versión en la nube
de Phabricator.

Phabricator, es una Herramienta Paga, pero en la modalidad de utilización de usuarios,


no por la herramienta.

Collaborator soporta un gran número de sistemas de control de versiones como


Subversion, Git, CVS, Mercurial, Perforce y TFS. Hace un buen trabajo al integrarse
con herramientas populares de gestión de proyectos y con IDE como Jira, Eclipse y
Visual Studio.

Esta herramienta también permite la presentación de informes y el análisis de métricas


clave relacionadas con su proceso de revisión de códigos. Además, Collaborator ayuda
en la gestión de la auditoría y el seguimiento de errores también. Si su pila tecnológica
incluye software empresarial y necesita soporte para configurar su proceso de revisión
de código, debería probar Collaborator. (Daityari, 2020, https://n9.cl/403aa).

Figura 7. CodeScene

Fuente: Daityari, 2020, https://n9.cl/403aa

Es una herramienta de revisión de código que va más allá del análisis de código
estático tradicional. Realiza análisis de código de comportamiento incluyendo una
dimensión temporal para analizar la evolución de su código base de código. CodeScene
está disponible en dos formas: una solución basada en la nube y una solución en las
instalaciones.

“Los planes de CodeScene basados en la nube empiezan gratis para los repositorios
públicos alojados en GitHub, pero luego se deberá abonar una suscripción”.
CodeScene procesa su historial de control de versiones para proporcionar
visualizaciones del código. Además, aplica algoritmos de aprendizaje de máquina para
identificar patrones sociales y riesgos ocultos en el código.

A través del historial de control de versiones, CodeScene perfila a cada miembro del
equipo para trazar su base de conocimientos y crear dependencias entre los equipos.
También introduce el concepto de puntos calientes en su repositorio, identificando los
archivos que experimentan mayor actividad de desarrollo. Estos hotspots (puntos
calientes) requieren la mayor atención en el futuro. (Daityari, 2020, https://n9.cl/403aa).

Figura 8. CodeScene

Fuente: Daityari, 2020, https://n9.cl/403aa

Figura 9. Visual Expert


Fuente: Daityari, 2020, https://n9.cl/403aa

Es una solución empresarial para la revisión de código especializada en código de base


de datos. Tiene soporte solo para tres plataformas: PowerBuilder, SQL Server y Oracle
PL/SQL. Si estás usando cualquier otro DBMS, no podrás integrar Visual Expert para la
revisión de código.

Además de una revisión de código tradicional, Visual Expert analiza cada cambio en su
código para prever cualquier problema de rendimiento debido a los cambios. La
herramienta puede generar automáticamente una documentación completa de su
aplicación a partir del código también.

Si estás usando PowerBuilder, SQL Server, u Oracle PL/SQL y deseas una herramienta
de revisión de código especializada para tus necesidades, deberías probar Visual
Expert. (Daityari, 2020, https://n9.cl/403aa).
Figura 10. Gerrit

Fuente: Daityari, 2020, https://n9.cl/403aa Daityari, 2020, https://n9.cl/403aa

Es una herramienta de revisión de código libre y de código abierto basada en la web


para los repositorios de Git, escrita en Java. Para ejecutar Gerrit, necesitas descargar
el código fuente y ejecutarlo en Java. Aquí está el proceso de instalación de una
versión autónoma de Gerrit.

Gerrit combina la funcionalidad de un rastreador de errores y una herramienta de


revisión en uno. Durante una revisión, los cambios se muestran uno al lado del otro en
un diff (se utiliza para la comparación de archivos) unificado, con la posibilidad de
iniciar una conversación por cada línea de código añadido. Esta herramienta funciona
como un paso intermedio entre un desarrollador y el repositorio central. Además, Gerrit
también incorpora un sistema de votación.
Si posees la experiencia técnica para instalar y configurar Gerrit, y estás buscando una
herramienta de revisión de código libre, debería servir como una solución ideal para tus
proyectos. (Daityari, 2020, https://n9.cl/403aa).

Figura 11. Rhodecode

Fuente: Daityari, 2020, https://n9.cl/403aa

Es una herramienta basada en la web que le ayuda a realizar revisiones de códigos.


Soporta tres sistemas de control de versiones: Mercurial, Git y Subversion.

Una versión de Rhodecode es una herramienta con suscripción.

El código de rodaje permite a un equipo colaborar eficazmente a través de revisiones


iterativas y conversacionales del código para mejorar la calidad del mismo. Esta
herramienta proporciona además una capa de gestión de permisos para un desarrollo
seguro.

Además, un registro de cambios visuales te ayuda a navegar por la historia de tu


proyecto a través de varias ramas. También se proporciona un editor de código en
línea para pequeños cambios a través de la interfaz web.

Rhodecode se integra perfectamente con tus proyectos existentes, lo que lo convierte


en una gran elección para alguien que busca una herramienta de revisión de código
basada en la web. Por lo tanto, la edición comunitaria es ideal para aquellos con
experiencia técnica que buscan una herramienta de revisión de código gratuita y
confiable. (Daityari, 2020, https://n9.cl/403aa).

Figura 12. Veracode

Fuente: Daityari, 2020, https://n9.cl/403aa


Proporciona un conjunto de herramientas de revisión de código que te permiten
automatizar las pruebas, acelerar el desarrollo, integrar un proceso de corrección y
mejorar la eficiencia de tu proyecto. El conjunto de herramientas de revisión de código
de Veracode se comercializa como una solución de seguridad que busca la
vulnerabilidad en sus sistemas. Proporcionan un conjunto de dos herramientas de
revisión de código:

Análisis estático: una herramienta que permite a los desarrolladores identificar y corregir
los fallos de seguridad en su código.

Análisis de composición de software: una herramienta que gestiona el proceso de


remediación y mitigación de los fallos en el código. (Daityari, 2020, https://n9.cl/403aa).

Figura 13. Reviewable

Fuente: Daityari, 2020, https://n9.cl/403aa


Es una herramienta de revisión de código para las solicitudes de extracción de GitHub.
Es una herramienta Gratis hasta un determinado número de Usuarios, a partir tiene un
valor de suscripción. Como la herramienta está integrada con GitHub, puedes iniciar
sesión con tu cuenta de GitHub y empezar a trabajar.

Una cosa interesante de Reviewable es que supera algunos inconvenientes de la


revisión de código en la función de solicitudes de extracción de GitHub. Por ejemplo, un
comentario en una línea de código es automáticamente ocultado por GitHub una vez
que un desarrollador cambia la línea porque GitHub asume que el problema ha sido
solucionado. Pero, en realidad, las cosas pueden ser diferentes.

Además, GitHub tiene límites de línea relativamente pequeños para mostrar las
diferencias de los archivos.

Si buscas una herramienta que sea coherente con GitHub, pero quieres más
características que peticiones de extracción, Reviewable debería ser tu herramienta de
trabajo. (Daityari, 2020, https://n9.cl/403aa).

Figura 14. Peer Review for Trac


Fuente: Daityari, 2020, https://n9.cl/403aa

Si utilizas Subversion, el plugin (complemento) “Peer Review for Trac” (revisión por
pares para la vista) proporciona una opción gratuita y de código abierto para realizar
revisiones de código en tus proyectos. El plugin de revisión por pares se integra en el
proyecto de código abierto Trac, que es un wiki y un sistema de seguimiento de
problemas para proyectos de desarrollo.

Trac integra el wiki y el rastreador de problemas con sus revisiones para proporcionar
una solución integral. Mientras que la funcionalidad básica de comparación de cambios
y conversación está disponible, el plugin te permite diseñar flujos de trabajos
personalizados para tus proyectos.

Por ejemplo, se podrían decidir las tareas a realizar en los activadores, como la
presentación de un cambio o la aprobación en una revisión de código. También puedes
crear informes personalizados sobre tus proyectos. (Daityari, 2020, https://n9.cl/403aa).

C O NT I NU A R
Lección 3 de 6

Unidad 2. Pruebas unitarias

Tema 1. ¿Qué son las pruebas unitarias?

En programación, una prueba unitaria es una forma de comprobar el correcto


funcionamiento de una unidad de código. Por ejemplo en diseño estructurado o en
diseño funcional una función o un procedimiento, en diseño orientado a objetos una
clase. Esto sirve para asegurar que cada unidad funcione correctamente y
eficientemente por separado. Además de verificar que el código hace lo que tiene que
hacer, verificamos que sea correcto el nombre, los nombres y tipos de los parámetros,
el tipo de lo que se devuelve, que, si el estado inicial es válido, entonces el estado final
es válido también. (Argentina.gob, s. f., https://n9.cl/kag2f).

En pocas palabras y lo más usual sería revisar el flujo correcto de los métodos de
nuestras clases a través de pruebas que vamos a definir para corroborar que el método
se comporte de la manera esperada antes distintos escenarios. Asimismo, si más
adelante se modifica nuestro método, se tendrá que corroborar los cambios a través de
las pruebas unitarias para que se acepte la modificación. (Rodríguez Patiño, 2020,
https://n9.cl/go830).

La idea es escribir casos de prueba para cada función no trivial o método en el módulo, de forma que
cada caso sea independiente del resto. Luego, con las pruebas de integración, se podrá asegurar el
correcto funcionamiento del sistema o subsistema en cuestión.
Características

Para que una prueba unitaria posea la “calidad suficiente” debe ser:

Automatizable.

Completa.

Repetible.

Independiente.

Profesional.

Ventajas

1. Proporciona un trabajo ágil. Como procedimiento ágil que es, te permite poder detectar
los errores a tiempo, de forma que puedas reescribir el código o corregir errores sin
necesidad de tener que volver al principio y rehacer el trabajo. Puesto que las pequeñas
se van haciendo periódicamente y en pequeños packs. Disminuyendo el tiempo y el
coste.

2. Calidad del código. Al realizar pruebas continuamente y detectar los errores, cuando el
código está terminado, es un código limpio y de calidad.

3. Detectar errores. A diferencia de otros procesos, los test unitarios nos permiten detectar
los errores rápidamente, analizamos el código por partes, haciendo pequeñas pruebas y
de manera periódica, además, las pruebas se pueden realizar las veces que hagan falta
hasta obtener el resultado óptimo.

4. Facilita los cambios y favorece la integración. Los test unitarios nos permiten modificar
partes del código sin afectar al conjunto, simplemente para poder solucionar bugs que
nos encontramos por el camino. Los test unitarios, al estar desglosados en bloques
individuales permiten la integración de nuevas aportaciones para hacer un código más
complejo o actualizarlo en función de lo que el cliente demande.

5. Proporciona información. Gracias al continuo flujo de información y la superación de


errores, se puede recopilar gran cantidad de información para evitar bugs venideros.

6. Proceso debugging (depuración de programas). Los test unitarios ayudan en el


proceso de debugging. Cuando se encuentra un error o bug en el código, solo es
necesario desglosar el trozo de código testeado. Este es uno de los motivos principales
por los que los test unitarios se hacen en pequeños trozos de código, simplificando
mucho la tarea de resolver problemas.

7. El diseño. Si primero se crean los test, es mucho más fácil saber con anterioridad cómo
debemos enfocar el diseño y ver qué necesidades debemos cumplir. Testeando una
pieza del código, también puedes saber que requisitos debe cumplir, y por eso mismo te
será mucho más fácil llegar a una cohesión entre el código y el diseño.

8. Reduce el coste. Partiendo de la base de que los errores se detectan a tiempo, lo cual
implica tener que escribir menos código, poder diseñar a la vez que se crea y optimizar
los tiempos de entrega, vemos una clara relación con una reducción económica.
(Apiumhub, 2018, https://n9.cl/bzlxt).

Las tres “A”

Cada caso de prueba debe implementar las siguientes tres “A” para que se puedan
ejecutar con éxito.

Arrange (organiza): organiza tu prueba para que se pueda comenzar con las pruebas
ya sea creando los parámetros u objetos mock (objetos ficticios que facilitan las
pruebas) para tu método o función.

Act (actúa): realiza la llamada a tu método o función.

Assert (confirmar): corrobora que el método o función probado se comporte de la


manera esperada. (Rodríguez Patiño, 2020, https://n9.cl/go830).

Limitaciones
Las pruebas unitarias no descubrirán todos los errores del código como, por ejemplo, de integración,
problemas de rendimiento y todo aquello que afecte al sistema en conjunto. Son efectivas si se utilizan
en conjunto con otras pruebas de software.

Es necesario saber que las pruebas unitarias por sí solas, no son perfectas, puesto que
comprueban el código en pequeños grupos, pero no la integración total del mismo. Para
ver si hay errores de integración es necesario realizar otro tipo de pruebas de software
conjuntas y de esta manera comprobar la efectividad total del código. (Apiumhub, 2018,
https://n9.cl/bzlxt).

Preguntas de reflexión

¿Cuáles son los escenarios más favorables para planear y ejecutar test automatizados?

Considerando que hoy en día las grandes empresas están migrando sus aplicaciones al
entorno web, ¿qué tipo de estrategia utilizarías para monitorear los desarrollos con respecto
a los test funcionales?

Tema 2. Diseño del test unitario

Desde el aspecto potestad, es decir, mirando los test según a quién le pertenecen,
distinguimos entre test escritos por desarrolladores y test escritos por el dueño del
producto. Recordemos que el dueño del producto es el analista de negocio o bien el
propio cliente. Lo ideal es que el analista de negocio ayude al cliente a escribir los test
para asegurarse de que las afirmaciones están totalmente libres de ambigüedad.
Los test que pertenecen al Dueño del Producto se llaman test de cliente o de
aceptación. Charlie Poole prefiere llamarles test de cliente, ya que por aceptación se
podría entender que se escriben al final cuando, realmente, no tiene que ser así. De
hecho, en TDD partimos de test de aceptación (ATDD) para conectar requerimientos
con implementación, o sea, que los escribimos, antes que nada. Cuando se escribe el
código que permite ejecutar este test, y se ejecuta positiva mente, se entiende que el
cliente acepta el resultado. Por esto se habla de aceptación. Y también por esto es un
término provocador, al haber clientes que niegan que un test de aceptación positivo
signifique que aceptan esa parte del producto. (Blé Jurado, 2010, https://n9.cl/81eup).

Figura 15. Producto

Fuente: Blé Jurado, 2010, https://n9.cl/oruw7


Las pruebas de software aumentan considerablemente la calidad de los sistemas,
contribuyendo a su posicionamiento en el mercado. Sin embargo, son costosas por lo
que se hace necesaria su automatización para disminuir su costo y aumentar su
efectividad. Específicamente, las pruebas unitarias se encargan de probar que las
unidades individuales del diseño de software, componente o módulo de software
funcionan correctamente.

Aunque existen herramientas que ejecutan pruebas unitarias de manera automática,


carecen de funcionalidades que asistan al desarrollador en el diseño de los casos de
prueba. El modelo MTest.search contiene procedimientos y métodos para la generación
y ejecución de casos de pruebas insertados en un entorno de integración continua
dentro del propio proceso de desarrollo de aplicaciones. (Larrosa, Delgado Dapena y
Fernández Oliva, 2018, https://n9.cl/2psv7).

¿Qué elementos prueba un test unitario?

Las pruebas unitarias aíslan una parte del código y comprueban que funciona correctamente. Son
pequeños test que valoran el comportamiento de un objeto. El unit testing suele realizarse durante la
fase de desarrollo de aplicaciones de software o móviles.

Desde el aspecto potestad, es decir, mirando los test según a quién le pertenecen,
distinguimos entre test escritos por desarrolladores y test escritos por el Dueño del
Producto. Recordemos que el Dueño del Producto es el analista de negocio o bien el
propio cliente. Lo ideal es que el analista de negocio ayude al cliente a escribir los test
para asegurarse de que las afirmaciones están totalmente libres de ambigüedad.
Los test que pertenecen al Dueño del Producto se llaman test de cliente o de
aceptación, esto tiene como referencia, ya que por aceptación se podría entender que
se escriben al final cuando, realmente, no tiene que ser así. De hecho, en TDD partimos
del test de aceptación (ATDD) para conectar requerimientos con implementación, o sea,
que los escribimos, antes que nada. Cuando se escribe el código que permite ejecutar
este test, y se ejecuta positivamente, se entiende que el cliente acepta el resultado.
Por esto se habla de aceptación. Y también por esto es un término provocador, al haber
clientes que niegan que un test de aceptación positivo signifique que aceptan esa parte
del producto.

Nosotros hablaremos de aceptación porque se usa más en la literatura que test de


cliente, aunque convendrá recordar lo peligrosa que puede llegar a ser esta
denominación. En el siguiente diagrama se muestra la clasificación de los test típica de
un entorno ATDD/TDD. A la izquierda, se agrupan los test que pertenecen a
desarrolladores y, a la derecha, los que pertenecen al dueño del producto. A su vez,
algunos tipos de test contienen otros.

Son los test más importantes para el practicante TDD, los ineludibles. Cada test unitario
o test unidad (unit test en inglés) es un paso que andamos en el camino de la
implementación del software. Todo test unitario debe ser:

Atómico.

Independiente.

Inocuo.

Rápido.

Si no cumple estas premisas entonces no es un test unitario, aunque se ejecute con


una herramienta tipo xUnit.

Atómico significa que el test prueba la mínima cantidad de funcionalidad posible. Esto
es, probará un solo comportamiento de un método de una clase. El mismo método
puede presentar distintas respuestas ante distintas entradas o distinto contexto. El test
unitario se ocupará exclusivamente de uno de esos comportamientos, es decir, de un
único camino de ejecución.
A veces, la llamada al método provoca que internamente se invoque a otros métodos;
cuando esto ocurre, decimos que el test tiene menor granularidad, o que es menos fino.
Lo ideal es que los test unitarios ataquen a métodos lo más planos posibles, es decir,
que prueben lo que es indivisible. La razón es que un test atómico evita tener que usar
el depurador para encontrar un defecto en el SUT, puesto que su causa será muy
evidente.

Como veremos en la parte práctica, hay veces que vale la pena ser menos estrictos
con la atomicidad del test, para evitar abusar de los dobles de prueba.

Independiente significa que un test no puede depender de otros para producir un


resultado satisfactorio. No puede ser parte de una secuencia de test que se deba
ejecutar en un determinado orden. Debe funcionar siempre igual independientemente de
que se ejecuten otros test o no.

Inocuo significa que no altera el estado del sistema. Al ejecutarlo una vez, produce
exactamente el mismo resultado que al ejecutarlo veinte veces. No altera la base de
datos, ni envía emails ni crea ficheros, ni los borra. Es como si no se hubiera
ejecutado.

Rápido tiene que ser porque ejecutamos un gran número de test en pocos minutos y se
ha demostrado que tener que esperar unos cuantos segundos cada rato, resulta muy
improductivo. Un solo test tendría que ejecutarse en una pequeña fracción de segundo.
La rapidez es tan importante que Kent Beck ha desarrollado recientemente una
herramienta que ejecuta los test desde el IDE Eclipse mientras escribimos código, para
evitar dejar de trabajar en código mientras esperamos por el resultado de la ejecución.
Se llama JUnit Max. Olof Bjarnason ha escrito otra similar y libre para Python.

Para conseguir cumplir estos requisitos, un test unitario aísla la parte del SUT que
necesita ejercitar de tal manera que el resto está inactivo durante la ejecución. Hay
principalmente dos formas de validar el resultado de la ejecución del test: validación del
estado y validación de la interacción, o del comportamiento. En los siguientes capítulos
los veremos en detalle con ejemplos de código.
Los desarrolladores utilizamos los test unitarios para asegurarnos de que el código
funciona como esperamos que funcione, al igual que el cliente usa los test de cliente
para asegurarse que los requisitos de negocio se alcancen como se espera que lo
hagan.

FIRST

Como los acrónimos no dejan de estar de moda, cabe destacar que las características
de los test unitarios también se agrupan bajo las siglas FIRST que vienen de: fast,
independent, repeatable, small y transparent (rápido, independiente, repetible,
pequeño y transparente)

Repetible encaja con inocuo, pequeño caza con atómico y transparente quiere decir que
el test debe comunicar perfectamente la intención del autor. (Blé Jurado, 2010,
https://n9.cl/81eup).

Tema 3. Framework de test unitario

Un framework de pruebas unitarias se define como una herramienta que permite escribir pruebas sobre
un bloque de código. Esta herramienta se ejecuta bajo un entorno de “javascript” sin necesidad de que
interfiera en el IDE ni en la propia aplicación.

Cuando estamos realizando pruebas unitarias sobre nuestro código, muchas veces es
complicado realizar pruebas de métodos que interactúan con componentes de terceros
o que requieren de la existencia de cierta información que no se dispone en la prueba.
Este tipo de problemática, bastante común en el desarrollo de pruebas unitarias, se
resuelve con herramientas que permiten simular una funcionalidad o sobrescribir un
comportamiento existente. Estos objetos simulados (mock) pueden implementarse
utilizando diferentes librerías las cuales cada una de ellas tiene sus ventajas y
desventajas.

Existen muchas herramientas en el mercado para realizar mock de clases y métodos.


No vamos a entrar en detalles sobre cada una de las funcionalidades de estos
frameworks ni en comparaciones detalladas de los mismos, ya que hay extensos
artículos en internet que resaltan las bondades de cada uno de ellos. Así que solo me
limitaré a dar una descripción rápida de las mismas y en nuestra opinión al respecto,
resaltando sus características (buenas o malas) luego de haberlos utilizado en distintos
proyectos.

Para efectos prácticos de este compilado, mostraremos algunos frameworks usados


para este propósito, estas son PHPUnit, Mockery y AspectMock. La idea es poder
mostrar una comparación simple de como escribir la misma acción en los diferentes
frameworks y poder comprar las ventajas y desventajas de cada uno de ellos.

Dentro del ámbito de las pruebas unitarias el framework PHPUnit es por lejos el más
utilizado por todos, el cual implementa todas las funcionalidades para desarrollar de
forma eficiente las pruebas requeridas. Este framework incluye los componentes que
permite realizar un mock de clases y métodos, que si bien tiene la mayoría de las
funcionalidades necesarias muchas veces es un poco tedioso escribir las mismas.

A continuación, veremos en detalle cómo simular un conjunto de métodos de una


clase:

PHPUnit

$mock= $this->getMock('Logger');

$mock->expects($this->once())

->method('addInfo')
->with($this->equalTo('bar'))

->will($this->returnValue(true));

Este ejemplo sencillo simula el método addInfo (agregar información) de la clase


Logger (registradora), validando que se llame una sola vez, recibiendo por parámetro
“bar”, y devolverá true como resultado. (Citroni, 2016, https://n9.cl/5izuh).

Tabla 1. Ventajas y desventajas

Ventajas Desventajas

+ No se necesita ninguna librería


externa, ya que viene incluida en - Sintaxis engorrosa.
PHPUnit.
- No cubre todas las funcionalidades
+ Extensa y necesarias.
detallada documentación.

Fuente: elaboración propia con base en Citroni, 2016, https://n9.cl/5izuh

Mockery

Una librería que se aplica sobre un framework de pruebas unitarias (como PHPUnit) y
que permite extender y simplificar todo lo relacionado con el “mockeo” (perdón RAE) de
objetos y clases. Si bien Mockery tiene una sintaxis muy simple de comprender,
dispone de un arsenal de funcionalidades que lo hacen un aliado poderoso a la hora de
simular objetos. El ejemplo anterior podemos expresarlo usando Mockery de la
siguiente forma:
$mock = Mockery::mock('Logger');

$mock->shouldReceive('addInfo')

->once()

->with('bar')

->andReturn(true);

Como se puede apreciar, la sintaxis para el “mockeo” del método es mucho más
sencilla y fácil de leer. Esto es un ejemplo muy simple, pero a medida que se hace más
complejo lo que se quiere simular (como resultados dinámicos, parámetros dinámicos, o
“mockeos” parciales) se torna mucho más evidente la simplicidad de sintaxis de la cual
dispone. (Citroni, 2016, https://n9.cl/5izuh).

Tabla 2. Ventajas y desventajas

Ventajas Desventajas

+ Sintaxis simple.

- Una librería más aparte


+ Muchas funcionalidades
del framework de prueba unitaria.
implementadas de forma muy
sencilla.
- No cubre todas las funcionalidades
necesarias.
+ Extensa y detallada
documentación.

Fuente: elaboración propia con base en Citroni, 2016, https://n9.cl/5izuh


AspectMock

Es un framework muy potente que permite realizar ciertas acciones que a los otros
frameworks le cuestan o le son imposibles. Si bien AspectMock no tiene la mayor
legibilidad permite realizar el mockeo de cualquier clase, incluyendo las del propio PHP,
así como también de funciones sueltas. Además, permite mockear clases estáticas, lo
cual puede ser de mucha ayuda en ciertos frameworks (como Laravel). En nuestro
ejemplo anterior, realizar el mockeo del método addInfo de la clase Logger se debería
implementar de la siguiente manera.

use AspectMock\Test as test;

$mock = test::double('Logger',

['addInfo' => true]); $mock->verifyInvokedOnce('addInfo', ['bar']);

Con una sintaxis muy reducida, que en ciertos casos puede volverse un poco compleja
de leer, AspectMock es el mejor aliado a la hora de mockear clases y métodos, ya que
permite realizar lo mismo que los frameworks anteriores con el agregado de poder
simular clases estáticas, funciones del sistema y la posibilidad de implementar TDD de
una manera muy simple. (Citroni, 2016, https://n9.cl/5izuh).

Tabla 3. Ventajas y desventajas

Ventajas Desventajas
Ventajas Desventajas

+ Sintaxis muy simple. - Una librería más aparte del


framework de prueba unitaria.
+ Cubre todas las funcionalidades
necesarias. - La documentación es muy básica.

Fuente: elaboración propia con base en Citroni, 2016, https://n9.cl/5izuh

Los frameworks para realizar el mock de clases y métodos son herramientas


esenciales en el desarrollo de cualquier aplicación que se precie, ya que nos permiten
realizar nuestras pruebas aislándonos de componentes que no podemos gestionar
(como un componente de terceros), o que tienen un comportamiento no determinista
(como leer información de un dispositivo externo) o el cual no queremos incluir en
nuestro test (porque otros test ya lo prueban).

Lo bueno de estos frameworks es que no necesitamos casarnos con uno solo de ellos,
sino que, como todos los aspectos de la tecnología, podemos utilizar el que mejor se
ajuste a nuestras necesidades en cada proyecto. (Citroni, 2016, https://n9.cl/5izuh).

Tema 4. Ejemplos y casos prácticos de test unitarios

Caso 1

La prueba unitaria debe ser capaz de cubrir todas las posibles funcionalidades en un mismo bloque de
código.
Tenemos un módulo que nos exporta el método “getObjectDescription” al que pasándole
como parámetro el “type” nos devuelve un “string”. Ahora bien, identifiquemos los
diferentes casos que se nos pueden dar con dicho módulo:

1. Qué “getObjectDescription” exista.

2. Llamar al método “getObjectDescription” con un parámetro que exista en el


“objectMapping”.

3. Llamar al método “getObjectDescription” con un parámetro que no exista en el


“objectMapping”.

4. Llamar al método “getObjectDescription” con un parámetro vacío.

5. Llamar al método “getObjectDescription” sin parámetro.

Estos son, a primera vista, los casos de uso que se pueden presentar en dicho bloque
de código. (Paradigma Digital, 2020, https://cutt.ly/5m0UkVv).

El código en cuestión es el siguiente.

Figura 16. Código


Fuente: elaboración propia.

Para los efectos de este ejemplo, utilizaremos JEST que está basado en Jasmine. Esto nos servirá para
demostrar bien el test que estamos planteando.

Es importante aclarar que esta aplicación es para JavaScript, por lo que requiere que el participante
tenga, por lo menos, un conocimiento mínimo de este lenguaje para poder comprender mejor el ejercicio.
Sin embargo, se ha comentado cada sección del código para que se pueda entender la funcionalidad y el
proceso que se busca alcanzar con él.

Empezamos con nuestra prueba.


Pero, antes, deberemos preparar nuestro fichero de test. Por lo general, los ficheros de
test se suelen ubicar en la misma carpeta donde se encuentra el fichero que vamos a
usar para las pruebas, poniéndole el mismo nombre seguido de .spec.js o .test.js. Por
regla general, es preferible tener separado los ficheros de pruebas de los de la
aplicación. Por ello, se suele crear una carpeta con los test a la misma altura de donde
se encuentran las fuentes, siguiendo una estructura similar a la contenida en src.
(Pérez, 2020, https://n9.cl/jw88r).

En el archivo donde tenemos el código para el test, tendremos lo siguiente:

Figura 17. Código

Fuente: Pérez, 2020, https://n9.cl/jw88r

Una vez que tengamos nuestra estructura de fichero, vamos con los test.

Figura 18. Código


Fuente: Pérez, 2020, https://n9.cl/jw88r

Llamar al método “getObjectDescription” con un parámetro que exista en el


“objectMapping”. Dicho método nos devuelve un string (cadena de caracteres) que nos
sirve para comprobar que lo que nos devuelve, es algo que existe: Deberemos
almacenarlo en una variable y comprobar dicha variable. (Pérez, 2020,
https://n9.cl/jw88r).

Figura 19. Código

Fuente: Pérez, 2020, https://n9.cl/jw88r


Figura 20. Código

Fuente: Pérez, 2020, https://n9.cl/jw88r

Denominar al método “getObjectDescription” con un parámetro vacío de igual manera que en el test
anterior.

Figura 21. Código

Fuente: Pérez, 2020, https://n9.cl/jw88r


Denominar al método “getObjectDescription” sin parámetro.

Figura 22. Código

Fuente: Pérez, 2020, https://n9.cl/jw88r

Siguiendo estos pasos, el test debería tener como resultado algo parecido a lo que muestra la siguiente
figura.

Figura 23. Código


Fuente: Pérez, 2020, https://n9.cl/jw88r
Escribir un test unitario no es algo complejo, pero sí que lleva su tiempo. Normalmente,
si en realizar una funcionalidad se tarda 1 hora, para realizar los test de dicha
funcionalidad se suele calcular multiplicando por tres el tiempo de desarrollo, ya que
tenemos que abarcar, a ser posible, un mínimo del 90 % de los posibles casos que se
puedan dar: por ejemplo, cuando el código se ejecuta correctamente o cuando
queremos controlar una excepción.

Todo ello conlleva unas acciones que, para tener un test correcto, hay que cubrir en la
mayoría de posibilidades o casos que se puedan dar en nuestro código. (Pérez, 2020,
https://n9.cl/jw88r).

Caso 2

Lo primero que vamos a hacer es crear un fichero con el código “javascript”, ya que
este será el código donde realizaremos los test.

Para este ejemplo, voy a crear un fichero llamado index.js.

export const sumar = (a, b) => a + b;

export const restar = (a, b) => a - b;

export const multiplicar = (a, b) => a * b;

export const dividir = (a, b) => a / b;

Continuamos creando una carpeta en nuestro proyecto llamada test, ya que allí
guardaremos todos nuestros test.
Una vez dentro de la carpeta test, crearemos un fichero y lo llamaremos igual que el
fichero donde está el código “javascript” en el que vamos a realizar el test.

El fichero quedaría de la siguiente forma: index.test.js

import { sumar, restar, multiplicar, dividir } from '../index.js';

describe('Operaciones matemáticas', () => {

test('Realizamos la suma', () => {

expect(sumar(1,1)).toBe(2);

});

test('Realizamos la resta', () => {

expect(restar(1,1)).toBe(0);

});

test('Realizamos la multiplicacion', () => {

expect(multiplicar(1,1)).toBe(1);

});

test('Realizamos la división', () => {

expect(dividir(1,1)).toBe(1);
});

});

Con nuestro fichero ya creado vamos a ejecutar el test, y para eso crearemos una tarea
en la sección scripts dentro del package.json:

"name": "JEST",

"version": "1.0.0",

"description": "",

"main": "index.js",

"scripts": {

"test": "jest"

},

"keywords": [],

"author": "",

"license": "ISC",

"devDependencies": {
"babel-jest": "^23.6.0",

"babel-polyfill": "^6.26.0",

"babel-preset-es2015": "^6.24.1",

"jest": "^23.6.0"

Ahora solo queda ejecutar nuestro test, y para ello usaremos la tarea que habíamos
creado en el package.json usando el siguiente comando: npm run test (test es el
nombre del key asociado al comando que ejecutará) o npm t (forma abreviada).

Si todo funciona correctamente, deberíamos ver en la pantalla el siguiente resultado:

> jest

PASS test/index.test.js

Operaciones matemáticas

√ Realizamos la suma (2ms).

√ Realizamos la resta.

√ Realizamos la multiplicación.

√ Realizamos la división.
Test Suites: 1 passed, 1 total.

Test: 4 passed, 4 total.

Snapshots: 0 total.

Time: 1.465 s.

Ran all test suites.

Jest usa los matchers para probar los diferentes valores que puede tener nuestro
código. (Tecnops.es, 2018, https://n9.cl/xd7rm).

Conclusión

Sin duda, los testeos son muy importantes y nos dan una tranquilidad muy grande a la
hora de poner un sistema en producción. Mientras más temprano obtengamos
resultados, más eficientes seremos en nuestro objetivo de garantizar el rendimiento
(performance) de un sistema.

Para aportar valor en una prueba, se necesita sumar mucha experiencia en pruebas y
tener una vocación muy grande por entender “a bajo nivel” cómo funcionan las cosas.
Esta variedad de skills (habilidades) que se necesitan para este tipo de rol es una de
las cosas que lo hace apasionante. Siempre aparecen tecnologías nuevas, siempre hay
soluciones más complejas, ¡siempre hay un desafío! Nosotros muchas veces decimos
que en este tipo de proyectos jugamos al “Doctor House”, ya que una tarea fundamental
es poder diagnosticar precisamente qué le pasa al sistema y sugerir posibles
soluciones. Y jugando a los médicos, debemos distinguir SÍNTOMAS de CAUSAS.
Nosotros queremos observar los síntomas, y con base en nuestra experiencia ser
capaces de determinar las causas y así poder recetar una solución a nuestro paciente,
a nuestro sistema bajo pruebas. Muchas veces esto se hace preguntando reiteradas
veces: ¿por qué sucede este síntoma? Por ejemplo:

Se observa que el tiempo de respuesta de una funcionalidad es mayor al aceptable.

¿Causa o síntoma?

Síntoma. Entonces, ¿por qué sucede?

Se analizan los tiempos discriminando cada acción de esa funcionalidad, y se observa


que el tiempo de respuesta de un componente de la aplicación se lleva la mayor parte.

¿Causa o síntoma?

Síntoma. Entonces, ¿por qué sucede?

Se analiza ese componente y se observa que el tiempo de respuesta de una SQL


ejecutada en ese componente es muy lento para lo que hace.

¿Causa o síntoma?

Síntoma. Entonces, ¿por qué sucede?

Luego de un análisis en conjunto con el encargado de la base de datos se observa la


falta de un índice en la base de datos.

¿Causa o síntoma?

Causa. Entonces, ¿cómo se soluciona?

Pasar de una causa a una solución generalmente es fácil, al menos para una persona
con experiencia en esa área. En este caso el experto de base de datos agregaría un
índice adecuado y esto resolvería la causa, que haría que se eviten los síntomas
presentados. De esta forma, con esta prueba, solucionamos el problema que
detectamos. De otro modo, los usuarios hubiesen sufrido esas demoras, y el experto de
base de datos hubiese tenido muchas quejas, y tendría que haber ajustado estos
parámetros en producción con los riesgos e impactos que esto pueda tener. (Toledo,
2014, pp. 169-170).
Preguntas de reflexión

Si tuvieras JEST como herramienta de gestión para automatizar las aplicaciones de un equipo de
desarrollo:

¿Cuál sería la vía más adecuada para crear la arquitectura de pruebas?

¿Con qué otro tipo de test podrías complementar el testing unitario y por qué?

Si en tu equipo de desarrollo se requiere hacer test: ¿qué modelo recomendarías?

Si tuvieras que definir en pocas palabras las bondades de los test automatizados: ¿qué dirías?

C O NT I NU A R
Lección 4 de 6

Glosario

Término Descripción

“Se trata de un conjunto


de definiciones y
protocolos que se utiliza
para desarrollar e integrar
el software de las
API aplicaciones, permitiendo
la comunicación entre dos
aplicaciones de software
a través de un conjunto de
reglas” (Red Hat, 2021,
https://n9.cl/ec4h).

ATDD “Acceptance Test Driven


Development. Esto
significa que todo el
equipo discute en
colaboración criterios de
aceptación, con ejemplos,
y luego los divide en un
conjunto de pruebas de
aceptación en concreto
antes de que comience el
desarrollo”. (Sarco, 2015,
https://n9.cl/srztn).

“Conjunto de prácticas
que combina las
operaciones de TI (OPS)
y el desarrollo de
software (DEV). Su
objetivo es acortar el ciclo
DEVOPS
de vida del desarrollo de
sistemas y proporcionar
una entrega continua con
software de alta calidad”.
(LinkFang.org, s. f.,
https://n9.cl/7bgig).

Es el entorno o marco de
Framework
trabajo

Es un Software de control
Git
de versiones.

Un entorno de desarrollo
integrado es una
aplicación que ofrece
IDE
servicios integrales que
simplifican el desarrollo
de software.

Interfaz En informática, el término


hace referencia a la
conexión física y
funcional que vincula dos
dispositivos o sistemas
independientes.

“Es un lenguaje de
programación y una
plataforma informática
comercializada por
primera vez en 1995 por
Sun Microsystems. Hay
Java muchas aplicaciones y
sitios web que no
funcionarán a menos que
tenga Java instalado y
cada día se crean más”
(Java, s. f.,
https://n9.cl/pndn).

Sistema de gestión de
bases de datos. En la
MySQL
actualidad, es el más
utilizado en el mundo.

PL/SQL “PL/SQL es el lenguaje de


programación que
proporciona Oracle para
extender el SQL estándar
con otro tipo de
instrucciones y elementos
propios de los lenguajes
de programación”
(MADEJA, s. f.,
https://n9.cl/xg9z).

“PostgreSQL es un
sistema de base de datos
relacional de alta
disponibilidad. Es capaz
de funcionar de manera
estable en el servidor y,
PostgreSQL
por lo tanto, resulta
robusto, una de las
principales características
que buscan las empresas”
(Arsys, 2018,
https://n9.cl/ouvvi).

“Conjunto de programas,
instrucciones y reglas
informáticas para ejecutar
Software
ciertas tareas en una
computadora” (RAE,
2020, https://n9.cl/c40v).

TDD “Test-driven
development (TDD) es
una práctica para
desarrollo de software
consistente en la
repetición de un ciclo
breve en el que primero
se codifica un caso para
automatizar la prueba de
la función que se quiere
programar” (Scrum
Manager, 2021,
https://n9.cl/g7krj).

Son las pruebas que se


realizan para conocer si
Test
un elemento cumple o no
su función.

“Microsoft TFS es una


solución de seguimiento
de trabajo, código
compartido y envío de
software repleta de
funciones que ofrece una
TFS amplia colección de
herramientas diseñadas
para facilitar y hacer más
eficiente el desarrollo de
software colaborativo”
(Visure, s. f.,
https://n9.cl/dj0oc).

WIKI Es un sistema de trabajo


que, en su mayoría,
conserva un historial de
cambios.

Unit testing Pruebas unitarias

Stack Prueba

Time out Cuelgue

Testing Proceso de prueba

Legacy Heredados

Petición pull Petición de empuje

Hotspots Puntos calientes

Se utiliza para la
Diff
comparación de archivos

Plugin Complemento

Revisión por pares para la


Peer Review for Trac
vista

Debugging Depuración de programas

Arrange Organiza

Objetos ficticios que


Objetos mock
facilitan las pruebas

Act Actúa

Assert Confirmar

Rápido, independiente,
Fast, independent, repeatable, small y transparent repetible, pequeño y
transparente
AddInfo Agregar información

Logger Registradora

String Cadena de caracteres

Performance Rendimiento

Skills Habilidades

C O NT I NU A R
Lección 5 de 6

Video de habilidades

Verify to continue
We detected a high number of errors from your
connection. To continue, please confirm that
you’re a human (and not a spambot).

I'm not a robot


reCAPTCHA
Privacy - Terms

Vamos a implementar las pruebas unitarias del microservicio que


desarrollamos en el módulo 2.

1. Debemos seleccionar el microservicio y realizar los pasos aprendidos en


el video.

2. Para cerrar la actividad, debemos enviar en forma de imagen el


resultado de la prueba del microservicio configurado.
Luego de hacer la actividad, verificá que hayas realizado correctamente las
consignas. Descargá el ítem de resolución a continuación:

Resolución M3.docx.pdf
663.6 KB

C O NT I NU A R
Lección 6 de 6

Referencias

Apiumhub (2018). Beneficios de las pruebas unitarias. Recuperado de: https://apiumhub.com/es/tech-


blog-barcelona/beneficios-de-las-pruebas-unitarias/

Arsys (2018). Por qué elegir PostgreSQL y llevarlo a Cloud. Recuperado de:
https://www.arsys.es/blog/soluciones/postgresql-servidores/

Blé Jurado, C. (2010). Diseño ágil con TDD. iExpertos. Recuperado de: https://docplayer.es/28469-
Diseno-agil-con-tdd-carlos-ble-jurado-y-colaboradores-prologo-de-jose-manuel-beas.html

Citroni, A. (26 de febrero de 2016). Frameworks de mock de objetos para pruebas unitarias. Folder It.
Recuperado de: https://folderit.net/es/blog/frameworks-de-mock-de-objetos-para-pruebas-unitarias-es/

Daityari, S. (30 de diciembre de 2020). Las 12 mejores herramientas de revisión de código para
desarrolladores (edición 2021). Kinsta Blog. Recuperado de: https://kinsta.com/es/blog/herramientas-de-
revision-de-codigo/

IBM Corp (2006). Herramientas y automatización de pruebas. Recuperado de:


https://cgrw01.cgr.go.cr/rup/RUP.es/SmallProjects/core.base_rup/guidances/concepts/test_automation_a
nd_tools_9A608794.html

Java (s. f.). ¿Qué es la tecnología Java y para qué la necesito? Recuperado de:
https://www.java.com/es/download/help/whatis_java.html

Larrosa, D., Delgado Dapena, M. y Fernández Oliva, P. (2018). Diseño y ejecución de pruebas
unitarias en diferentes lenguajes. XVII Convención y Feria Internacional Informática 2018: Universidad
Tecnológica de La Habana. Recuperado de:
https://www.researchgate.net/publication/324017462_DISENO_Y_EJECUCION_DE_PRUEBAS_UNITARI
AS_EN_DIFERENTES_LENGUAJES

Paradigma Digital (5 de noviembre de 2020). ¿Cómo hacer test unitarios en Javascript? Una pequeña
introducción. Paradigma Digital. Recuperado de: https://paradigma-digital.medium.com/cómo-hacer-test-
unitarios-en-javascript-una-pequeña-introducción-e9af3e7a0c50

Pérez, B. (2020). Test unitarios en Javascript: una pequeña introducción. Paradigma Digital. Recuperado
de: https://www.paradigmadigital.com/dev/test-unitarios-javascript-introduccion/

Real Academia Española (2020). Software [Definición]. Diccionario de la lengua española. Recuperado
de: https://dle.rae.es/software

Red Hat (2021). ¿Qué son las API y para qué sirven? Recuperado de:
https://www.redhat.com/es/topics/api/what-are-application-programming-interfaces

Sarco, J. P. (2015). TDD vs. BDD vs. ATDD. Recuperado de:


https://josepablosarco.wordpress.com/2015/03/31/tdd-vs-bdd-vs-atdd/

Scrum Manager (2021). TDD. Recuperado de: https://www.scrummanager.net/bok/index.php/TDD

Soto Morales, V. M. (20 de mayo de 2021). Conoce qué son las pruebas no funcionales de software.
Academia Pragma. Recuperado de: https://www.pragma.com.co/blog/conoce-que-son-las-pruebas-no-
funcionales-de-software

Tecnops (2018). ¿Por qué hacer testing en Javascript? Tecnops. Recuperado de:
https://tecnops.es/testing-en-javascript-con-jest-parte-1-de-2/

Tester House (26 de marzo de 2019). Pruebas funcionales / No funcionales. ¿Qué son y para qué
sirven? Tester House. Recuperado de: https://testerhouse.com/teoria-testing/pruebas-funcionales/
Toledo, F. (2014). Introducción a las pruebas de sistemas de información. Montevideo: Abstracta.
Recuperado de: https://studylib.es/doc/7273511/introducción-a-las-pruebas-de-sistemas-de-información

Vargas, C. (s. f.). Tipos de pruebas funcionales para el aseguramiento de la calidad. Trycore.
Recuperado de: https://trycore.co/transformacion-digital/tipos-de-pruebas-funcionales/

Visure (s. f.). Microsoft TFS (Azure DevOps). Recuperado de: https://visuresolutions.com/es/microsoft-
tfs-azure/

C O NT I NU A R

También podría gustarte