📌 En síntesis: Implementación y casos de uso prácticos de pilas (LIFO) y colas (FIFO), operaciones push, pop, enqueue, dequeue y manejo de desbordamiento.
Guía técnica desarrollada por el equipo de Proyecto Luna para formación profesional y consulta técnica abierta.
📌
📌 Aprendizaje esperado
📌
Generar reportes de testing según requerimientos, contrastando resultados obtenidos y esperados.
Durante esta semana nos centraremos en el concepto de testing en programación. Comprenderemos su importancia en la validación de soluciones, la detección de errores y el aseguramiento de la calidad del código, aspectos fundamentales para desarrollar programas confiables y eficientes. Testing no solo permite descubrir fallos antes de que el sistema esté en producción, sino que también contribuye al diseño de software más robusto y mantenible. Al aprender a testear, el o la estudiante comienza a pensar críticamente sobre el comportamiento esperado de una función, anticipando posibles errores o condiciones que podrían afectar su ejecución. De esta manera, el testing no es una etapa final, sino una parte integral del proceso de desarrollo. En Python, existen diversas formas de aplicar pruebas. Entre las más simples y efectivas encontramos:
🚀 Introducción
-
Las pruebas manuales, en las cuales se ejecuta el programa con ciertos datos de entrada y se observa si el resultado es correcto. 2. El uso del comando assert, que automatiza la validación de resultados comparando lo obtenido con lo esperado. 3. La comparación esperada vs. obtenida, técnica especialmente útil en contextos educativos, ya que permite al estudiante reflexionar sobre el porqué de un resultado incorrecto. 4. A través de ejercicios prácticos, los y las estudiantes aplicarán estos métodos en funciones con errores intencionales, aprendiendo a identificar errores lógicos, usar estructuras de control para evaluar condiciones, y registrar evidencias del comportamiento del código. 5. Además, se fomentará la depuración del código (debugging) como habilidad técnica, utilizando herramientas básicas como print() o el seguimiento paso a paso en entornos de desarrollo como Visual Studio Code. Esto permitirá que los y las estudiantes exploren activamente cómo fluye el programa, facilitando la corrección de errores y la mejora continua del diseño lógico.
-
Esta semana también promoverá una reflexión crítica mediante actividades como foros de cierre, en los que se compartirán experiencias y aprendizajes relacionados con el proceso de testeo. 7. En conjunto, esta unidad busca que el estudiante no solo sea capaz de hacer que su código funcione, sino que también sea capaz de demostrar por qué funciona, en qué condiciones falla, y cómo puede mejorarse, lo que representa una competencia clave en su formación como programador o programadora.
🧠 Concepto de testing
Testing es el proceso de verificar que un programa o una función se comporte como se espera. Su objetivo principal es detectar errores (también llamados bugs) antes de que el software llegue al usuario final. A través del testing, se comprueba si el código produce los resultados correctos bajo distintas condiciones de entrada, lo que permite aumentar la confiabilidad del sistema. Además de identificar fallos, el testing ayuda a validar que el software cumple con los requisitos funcionales, mejora la documentación del comportamiento esperado y facilita futuras modificaciones o mantenimiento, permitiendo aplicar las reglas del negocio detectadas en el levantamiento de requerimientos. En entornos de desarrollo profesional, se considera una práctica esencial, ya que previene pérdidas económicas, mejora la experiencia del usuario y refuerza la seguridad del sistema. En la Figura 1 destacamos las fases clave en el testing software.
Figura 1. Fases clave en el testing de software
Pregunta / Tema ¿Este código está haciendo lo que debe? Verificar si una función cumple correctamente su propósito. Usar assert para comprobar que funciona como se espera. Confirmar que la función responde correctamente a entradas válidas. Si alguna condición no se cumple, el programa lanza un error indicando un fallo en la función. Permite encontrar errores de forma temprana y evitar problemas futuros. ¿Qué ocurre si ingreso un dato diferente? Probar entradas inesperadas o poco comunes, como números negativos o tipos de dato incorrectos. Verificar si la función reconoce correctamente los números negativos y reflexionar sobrecómo interpreta el cero, que en matemáticas no es positivo ni negativo. Permite validar si la lógica cubre correctamente todas las posibilidades. ¿Qué pasa si el valor ingresado es cero o nulo? Considerar qué sucede si se recibe un valor inesperado como 0, un texto vacío (“”) o None. El programa debe devolver un resultado válido o un mensaje claro, sin fallar. Comprobar cómo maneja la función casos límite. Por ejemplo, verificar si es_positivo trata correctamente el cero y si saludar devuelve un saludo adecuado cuando no recibe un nombre. Esto ayuda a anticiparse a errores, promueve la programación preventiva y refuerza la responsabilidad de escribir código seguro y claro. Descripción Propósito de la prueba Tabla 1. Preguntas orientadoras para validar el comportamiento de un código Testing no es solo una etapa final: debe estar presente desde las primeras fases del desarrollo. Adoptar una mentalidad de prueba desde el inicio permite construir programas más sólidos, considerando todas las opciones posibles y detectar errores con menor costo. En programación, especialmente en Python, el testing permite responder a distintas preguntas que detallamos en la Tabla 1.
1.1. ¿Este código está haciendo lo que debe? Esta es la pregunta central del testing: verificar si una función cumple correctamente su propósito. Supongamos que diseñamos una función es_par(numero) para saber si un número es par. Podemos probarla usando assert, que es una palabra reservada del lenguaje, cuyo objetivo es hacer comparaciones para validar resultados entregando un verdadero (True) o falso (False), y para asegurarnos de que funcione como esperamos. 1.2. ¿Qué ocurre si ingreso un dato diferente? Es fundamental probar entradas inesperadas o poco comunes. Por ejemplo, si creamos una función es_positivo(numero), debemos verificar qué ocurre si ingresamos un número negativo o incluso un tipo de dato incorrecto. En el siguiente ejemplo, escribimos assert es_positivo(-5) == False, lo que significa que esperamos que al evaluar -5, la función responda que no es positivo. Esto prueba si la función es capaz de reconocer correctamente los números negativos. La Figura 2 significa que esperamos que al pasarle el número 2, la función devuelva True porque 2 es par, y que al pasarle el número 3, devuelva False porque 3 es impar. Si alguna de estas condiciones no se cumple, el programa lanzará un error, lo que nos indica que hay un fallo en la función y que debemos revisarla. Estas pruebas ayudan a garantizar que el código se comporte correctamente en distintos escenarios, permitiendo encontrar errores de forma temprana y evitar problemas futuros. Estas pruebas nos permiten confirmar que la función responde correctamente a diferentes entradas. Si alguna prueba falla, sabremos que existe un error lógico que debemos corregir.
Figura 2. Testing básico en Python usando assert
Luego, se hace otra prueba con assert es_positivo(0) == False, lo que nos lleva a reflexionar sobre un caso particular: ¿qué pasa con el cero?. En matemáticas, el cero no se considera un número positivo ni negativo, así que esta prueba verifica cómo lo está interpretando la función. Es importante incluir este tipo de casos en las pruebas porque ayudan a validar si la lógica implementada cubre correctamente todas las posibilidades. A continuación, se escribe la implementación de estas funcionalidades con assert: Estas pruebas ayudan a confirmar que la función responde correctamente ante distintos valores y no falla con entradas atípicas. El testing también permite prever y manejar errores sin que el programa se detenga inesperadamente. 1.3. ¿Qué pasa si el valor ingresado es cero o nulo? Muchos errores ocurren cuando el código no considera qué hacer si recibe un valor inesperado como 0, un texto vacío (“”) o None. Un buen programa debe estar preparado para estos escenarios, devolviendo un resultado válido o mostrando un mensaje claro, sin fallar. En la Figura 4 destacamos ejemplos de pruebas.
Figura 3. Testing de condiciones: positivos, negativos y cero
Figura 4. Validación de funciones frente a entradas especiales o no convencionales,
como el cero o cadenas vacías
Por ejemplo, al probar la función es_positivo, podemos evaluar cómo responde cuando recibe el número 0. Escribimos assert es_positivo(0) == False, con lo cual estamos comprobando si la función reconoce correctamente que el cero no es un número positivo. Este tipo de pruebas son importantes porque, aunque el cero no sea negativo, tampoco se considera positivo en matemáticas, y es necesario verificar si la función maneja adecuadamente este caso límite. Otro ejemplo es cuando tenemos una función llamada saludar(nombre), que recibe un nombre y devuelve un saludo. Si probamos con assert saludar(“”) == “Hola “, estamos revisando qué sucede si el nombre ingresado está vacío. En este caso, la función parece devolver el texto “Hola “ dejando un espacio al final, ya que no hay un nombre para completar el saludo. Este tipo de prueba permite identificar cómo responde el programa ante entradas inusuales, como un valor en blanco o un texto vacío. Al anticipar estas situaciones, el código se vuelve más robusto y preparado para evitar errores en la ejecución real. Estos casos ayudan a desarrollar la capacidad de anticiparse a errores, promueven una programación preventiva y refuerzan la responsabilidad del desarrollador de construir código seguro, funcional y claro 1.4 . Ventajas de la aplicación de pruebas: 1.4.1. Validar que el resultado de una función sea correcto El objetivo principal del testing es asegurarse de que una función realice correctamente su tarea en distintos escenarios. Esto se logra al comparar el resultado que se obtiene con el resultado que se espera para diferentes valores de entrada. Si una función falla con ciertos datos, el error puede corregirse antes de seguir avanzando, lo que permite detectar fallos lógicos incluso cuando el código parece funcionar correctamente a simple vista.
Ejemplo: A continuación, se ve la implementación de cada prueba escrita con assert Este tipo de testing permite validar el comportamiento de la función y asegurar su confiabilidad en situaciones variadas. 1.4.2. Prevenir futuros errores al modificar el código: Regresión El testing también sirve como un sistema de seguridad. Cuando modificamos un código existente (por mantenimiento o mejora), es posible que sin darnos cuenta afectemos otras partes del programa. Si contamos con pruebas bien definidas, estas nos advertirán de inmediato si algo dejó de funcionar como debía. Esto se llama regresión: un error nuevo que aparece por cambiar algo viejo.
Figura 5. Validación de la función calcular_doble con números positivos,
negativos y el caso cero
Si tienes una función calcular_doble(numero), deberías probar si entrega el resultado correcto con distintos casos: valores positivos, negativos, o incluso cero.
Con la instrucción assert calcular_doble(3) == 6, verificamos si la función devuelve 6 cuando recibe un 3 como entrada, ya que el doble de 3 es 6.
Luego, se prueba con un número negativo: assert calcular_doble(-2) == -4. Esto comprueba que la función también maneja correctamente los números negativos, devolviendo el doble de -2, que es -4.
Finalmente, se realiza una prueba con el valor cero: assert calcular_doble(0) == 0. Esta evaluación permite confirmar que la función respeta la propiedad matemática de que el doble de cero es cero.
1.4.4. Documentar qué se espera de cada función Las pruebas también actúan como una forma de documentación viva: muestran qué entradas se esperan y qué salidas deberían generarse. Al ver los casos de prueba de una función, otros programadores (o el mismo autor en el futuro) pueden entender cómo debe comportarse esa función, sin necesidad de leer toda su lógica interna. 1.4.3 Reducir el tiempo de depuración (debugging). Cuando se realiza testing constante, los errores se detectan de forma más rápida y precisa, lo que disminuye las horas dedicadas a buscar “por qué no funciona”. Con pruebas automatizadas (como assert) se puede identificar en qué parte específica del código falló una función. Esto permite enfocarse en soluciones y no en búsqueda de fallos desconocidos. 1.4.3 Reducir el tiempo de depuración (debugging). Cuando se realiza testing constante, los errores se detectan de forma más rápida y precisa, lo que disminuye las horas dedicadas a buscar “por qué no funciona”. Con pruebas automatizadas (como assert) se puede identificar en qué parte específica del código falló una función. Esto permite enfocarse en soluciones y no en búsqueda de fallos desconocidos.
Figura 6. Ventajas de las pruebas automatizadas
Además, esto ayuda en proyectos colaborativos y al reutilizar funciones. En conjunto, estas buenas prácticas convierten el testing en una herramienta esencial para cualquier desarrollador/a, no solo para corregir errores, sino también para construir software más robusto, claro, escalable y confiable.
📌 Técnicas básicas de testing
2.1. Pruebas manuales Las pruebas manuales consisten en ejecutar el código directamente y observar si los resultados son correctos de acuerdo con lo esperado. Son las más simples y se usan para probar rápidamente una función sin automatizar nada. En este ejemplo se define nuevamente la función sumar, que recibe dos parámetros a y b, y devuelve la suma de ambos utilizando return a + b. Para comprobar que la función está operando correctamente, se realiza una llamada a sumar(2, 3) y se imprime el resultado con print. Al ejecutar este código, debería mostrarse en pantalla el valor 5, ya que es la suma de 2 más 3. El comentario # ¿Debería mostrar 5? invita a reflexionar sobre el resultado esperado, lo que constituye una forma básica de verificación manual. Pero este tipo de prueba requiere que el desarrollador analice el resultado a mano, por lo que puede ser poco confiable para grandes volúmenes de pruebas.
Figura 7. Ejecución y verificación manual de funciones en Python
2.2. assert Validar automáticamente que el resultado obtenido coincide con el esperado, a través de la palabra reservada assert.
Figura 8. Ejecución y verificación manual de funciones en Python (Fuente: OpenAI (2023))
En este caso se define una función llamada restar que recibe dos parámetros, a y b, y devuelve el resultado de a - b, es decir, la resta del primer valor menos el segundo. Para comprobar que esta función funciona correctamente, se realizan varias pruebas utilizando assert. La primera prueba es assert restar(5, 2) == 3, lo cual es correcto porque 5 menos 2 es igual a 3. La segunda prueba, assert restar(2, 5) == -3, también es correcta, ya que 2 menos 5 da como resultado -3. Estas dos pruebas confirman que la función responde bien tanto cuando el resultado es positivo como cuando es negativo. Sin embargo, la tercera prueba es un ejemplo de error intencional para mostrar cómo se comporta assert cuando la condición no se cumple. En assert restar(2, 2) == 1, se está verificando si la resta de 2 menos 2 es igual a 1, lo cual es incorrecto, ya que el resultado real es 0. Debido a esto, Python lanza una excepción, que es un error en tiempo de ejecución, llamada AssertionError, indicando que la prueba ha fallado. Este error es una forma automática de señalar que hay un problema en la prueba o en el código, y es una herramienta fundamental en el testing, ya que permite detectar errores de manera inmediata y detener la ejecución si algo no funciona como se espera.Esto es muy útil para automatizar pruebas y detectar rápidamente errores. Además, dentro de los estándares de la programación moderna, se usa mucho en desarrollo profesional, especialmente en testing unitario.
2.3. Comparación esperada/obtenida Usar ejemplos para verificar el comportamiento. Esta técnica consiste en formular un conjunto de casos de prueba donde se especifican: En este ejemplo que se visualiza a continuación, se está evaluando la función sumar con diferentes combinaciones de números. Para cada entrada se anota el resultado esperado, es decir, el valor que la función debería devolver si está correctamente programada. Luego se compara con el resultado obtenido al ejecutar realmente la función. En los primeros tres casos de la tabla de mas abajo, el comportamiento es correcto. Por ejemplo, al sumar 1 y 2 se espera un 3, y efectivamente el programa devuelve 3. Lo mismo sucede con sumar(0,0) y sumar(-1,1), donde los resultados coinciden con lo esperado. Sin embargo, en la última fila se presenta un error lógico: al ejecutar sumar(2,2), el resultado esperado es 5, pero el programa devuelve 4. Esto indica que hay un problema en el código o en la lógica utilizada, ya sea porque la función está mal implementada o porque se cometió un error al definir el caso de prueba. Este tipo de tablas permite identificar rápidamente en qué momento el programa no se comporta como debería, facilitando la tarea de corregir fallos y mejorar la calidad del software.
La entrada.
El resultado esperado
El resultado obtenido
Una conclusión
Depuración (debugging): revisar paso a paso el flujo lógico para identificar errores.
sumar (1,2) Correcto Resultado esperado Resultado obtenido Conclusión Entrada Tabla 2. Ejemplo de pruebas manuales con resultados esperados y obtenidos Fuente: creación propia sumar (0,0) Correcto sumar (-1,1) Correcto sumar (2,2) Error lógico
🔹 Depuración (debugging)
La depuración es el proceso de revisar paso a paso el código para encontrar dónde y por qué se produce un error. Se puede hacer de manera manual (con print()) o con herramientas como el depurador de VS Code.
Figura 9. Multiplicación con retroalimentación visual en Python (Fuente: OpenAI (2023))
Estas técnicas permiten a estudiantes y profesionales:
Desarrollar con mayor confianza.
Detectar errores de manera anticipada.
Aumentar la calidad de sus programas. En este ejemplo se define una función llamada multiplicar que recibe dos valores como entrada, a y b. La función realiza la operación de multiplicación entre estos dos números y guarda el resultado en una variable llamada resultado. Después, utiliza un print con formato (f-string) para mostrar en pantalla un mensaje detallado: indica qué valores se están multiplicando y cuál es el resultado de la operación. Esto permite observar de forma directa qué está haciendo el programa en cada llamada a la función, lo cual es muy útil durante el proceso de aprendizaje o al depurar código. Finalmente, la función devuelve el valor almacenado en resultado mediante la instrucción return. Esto significa que la función no solo imprime el resultado en pantalla, sino que también lo entrega como valor de salida, permitiendo que se pueda utilizar en otras partes del programa si es necesario. Este ejemplo es sencillo pero importante, porque combina el cálculo con la visualización del proceso, facilitando la comprensión de cómo operan las funciones en Python y cómo se pueden usar los mensajes para verificar lo que ocurre en cada paso de la ejecución.
📌 Testing con unittest y pytest
El testing es el proceso de verificar que un programa o función se comporta como se espera. Permite detectar errores, validar resultados y asegurar la calidad del software. En Python, existen herramientas como ‘unittest’ y ‘pytest’ para facilitar este proceso mediante pruebas automatizadas.
🔹 Testing con unittest (incluido en Python)
Permite ejecutar código solo si una condición es verdadera. Por ejemplo, en el código mostrado a continuación ,unittest es un módulo de Python que permite crear y organizar pruebas unitarias de forma estructurada. Se basa en clases que agrupan pruebas y utilizan métodos como assertEqual o assertTrue. Ejemplo con unittest
Figura 10. Ejemplo básico de test unitario con unittest (Fuente: Creación propia)
En este ejemplo primero se define una función llamada saludar, que recibe un nombre como parámetro y devuelve un saludo personalizado. Por ejemplo, si se le pasa el valor “Ana”, la función devolverá “Hola Ana”. Luego se crea una clase llamada TestSaludar, que hereda o extiende de unittest.TestCase. Esta clase representa un conjunto de pruebas específicas relacionadas con la función saludar. Dentro de la clase, se define un método llamado test_saludo_correcto, el cual comprueba si la función está devolviendo el resultado esperado. En este caso, se utiliza el método self.assertEqual, propio de unittest, para comparar el valor que devuelve saludar(“Ana”) con el texto “Hola Ana”. Si los valores son iguales, la prueba se considera exitosa; si no lo son, el sistema reportará un error indicando que la función no funciona como se espera.– Finalmente, se incluye la instrucción if name == “main”:, que es una forma estándar de ejecutar el bloque de código principal en Python. Esto asegura que cuando se ejecute el archivo, se ejecuten automáticamente todas las pruebas definidas dentro de la clase TestSaludar mediante la función unittest.main(). Así, el programa realizará el test de manera automática y mostrará en consola un resumen indicando si la prueba pasó correctamente o si hubo algún fallo. En el código edad = 15, si evaluamos if edad < 18:, se ejecutará la línea que imprime “Descuento juvenil”, ya que la condición es verdadera. En caso contrario, el programa imprimiría “Descuento estándar”. Esta estructura permite controlar dos opciones posibles: cuando la condición se cumple y cuando no. Para ejecutar: guarda el archivo y ejecuta con python nombre_archivo.py. 3.2. Testing con pytest (requiere instalación) pytest es una herramienta externa más moderna y simple para realizar pruebas unitarias. Es muy utilizada por su facilidad de uso y resultados detallados. Se instala con pip.
Instalación: En este ejemplo se define una función llamada es_par que recibe un número n y devuelve el valor de la expresión n % 2 == 0. Esta operación comprueba si el residuo o resto número enteros, al dividir n entre 2 es igual a cero. Si es así, significa que el número es par y la función devuelve True; si no, devuelve False. Para verificar que la función funciona correctamente, se define otra función llamada test_par. Dentro de esta función se escriben dos pruebas usando assert. La primera prueba evalúa si es_par(4) devuelve True, ya que 4 es un número par. La segunda prueba verifica si es_par(3) devuelve False, ya que 3 es impar. Si ambas afirmaciones se cumplen, las pruebas terminan sin errores. Para ejecutar: guarda el archivo como test_funciones.py y corre pytest en la terminal.
Figura 11. Instalación de pytest con pip
Ejemplo con pytest:
Figura 12. Testing básico de paridad con assert
Tabla 3. Diferencias clave entre unittest y pytest Fuente: Creación propia Comparación rápida Característica unittest pytest Instalación Viene con Python Requiere instalación con pip Estilo Orientado a clases Basado en funciones Simplicidad Más estructurado Más simple y directo Mensaje de error Más técnicos Más legibles
📌 Testing con unittest y pytest
Caso de estudio Imagina que un cliente te solicita una función para verificar si un número entero es par o impar. La función debe llamarse ‘es_par’ y debe devolver True si el número es par y False si es impar. Antes de entregar el programa, debes asegurarte de que funcione correctamente con distintos tipos de entrada numérica. Objetivo Aplicar técnicas básicas de testing en Python utilizando assert para validar el comportamiento de una función simple ante diversos escenarios. Código de la función Casos de prueba con assert
Figura 13. Definición de función y llamada pendiente
Explicación del ejercicio Cada línea con ‘assert’ representa una prueba automática. Si la condición se cumple, el programa continúa sin errores. Si la condición es falsa, Python lanzará un ‘AssertionError’, indicando que algo está mal con la lógica de la función. Estas pruebas permiten comprobar que la función funciona como se espera en diferentes situaciones: con números positivos, negativos, impares y el caso especial del cero.
Figura 14. Pruebas manuales de la función es_par: casos de prueba
Recomendación Es recomendable que, al desarrollar cualquier función, crees casos de prueba que incluyan valores normales y también casos extremos. Esto fortalece tus habilidades de programación y asegura que entregas código funcional y confiable.
Durante esta semana, los y las estudiantes han identificado, analizado y corregido errores en funciones mediante pruebas manuales y automatizadas. El uso del testing les permite validar el comportamiento del código y mejorar la calidad de sus soluciones. El cierre reflexivo fortalece la conexión entre la teoría y la práctica, y prepara para futuros proyectos de desarrollo. Probar el código de forma rigurosa es un compromiso con la calidad, la seguridad y la experiencia del usuario final. A través del testing, los estudiantes no solo adquieren habilidades técnicas, sino también responsabilidad y rigor profesional, valores esenciales en cualquier disciplina tecnológica. Dominar estas técnicas proporciona una base sólida para enfrentar desafíos más complejos en proyectos reales, donde la estabilidad, la eficiencia y la capacidad de evolución del software son requisitos fundamentales.
💡 Conclusión
🚀 Potencia tus proyectos tecnológicos con Proyecto Luna
En Proyecto Luna desarrollamos arquitectura de software de alto rendimiento, optimización de bases de datos críticas y soluciones aplicadas de inteligencia artificial y automatización para empresas y profesionales.
- 💼 Soluciones de ingeniería: Conoce nuestras capacidades y casos de estudio en proyectoluna.com/work/.
- 🛡️ Seguridad y auditoría: Orientación técnica y respuesta ante incidentes en ¿Te han hackeado?.
- 📩 Contacto directo: Escríbenos a través de nuestro formulario de contacto o a
contacto@proyectoluna.com.
📚 Fuentes y lecturas recomendadas
-
Chazallet, M. (2020). Programación orientada a objetos en Python. Ediciones Informáticas.
-
Downey, A. (2002). Aprenda a pensar como un programador con Python. Green Tea Press.
-
Marzal, M., y García, R. (2011). Fundamentos de programación orientada a objetos. Pearson.





