Menu iconMenu icon
Python y SQL Biblia

Capítulo 11: Pruebas en Python

11.2 Simulación y parcheo

Simulación es una técnica esencial en las pruebas de software donde reemplazas partes de tu sistema con objetos simulados y haces afirmaciones sobre cómo se usaron. Este enfoque te permite simular el comportamiento de tu sistema sin involucrar todas sus componentes, lo que puede ser lento e ineficiente.

Para implementar la simulación, puedes usar unittest.mock, una biblioteca para pruebas en Python. Esta biblioteca proporciona un conjunto extenso de herramientas para crear y usar objetos simulados con facilidad, lo que te permite reemplazar partes de tu sistema en prueba y hacer afirmaciones sobre cómo se han utilizado.

Un objeto simulado es un objeto ficticio y flexible que actúa como sustituto de un objeto real. Devuelve a sí mismo cada vez que llamas a cualquier método o accedes a cualquier atributo, y registra qué métodos se llamaron y cuáles fueron los parámetros. Esto lo convierte en una excelente herramienta para simular comportamientos complejos y probar casos límite en tu código, lo que te permite detectar errores temprano en el proceso de desarrollo.

Ejemplo:

Aquí tienes un ejemplo sencillo para mostrar cómo podrías usar la simulación:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Parcheo es una técnica comúnmente utilizada en el desarrollo de software, especialmente en pruebas unitarias. Permite a los desarrolladores reemplazar un método o un atributo en un módulo o una clase con un nuevo objeto, lo que puede ser particularmente útil al probar código que depende de sistemas externos o recursos que pueden no estar disponibles o ser poco confiables. Al reemplazar estas dependencias con objetos simulados, los desarrolladores pueden simular el comportamiento del sistema externo o recurso, lo que les permite probar su código de manera aislada y detectar posibles problemas temprano.

Un aspecto importante a tener en cuenta al parchear es asegurarse de que el nuevo objeto que se está utilizando como reemplazo imite correctamente el comportamiento del objeto original que se está reemplazando. Esto a menudo implica crear un objeto simulado personalizado que implemente la misma interfaz o herede de la misma clase base que el objeto original, y luego anular o simular los métodos o atributos relevantes.

Además de las pruebas unitarias, el parcheo también se puede utilizar en otras áreas del desarrollo de software, como las pruebas de integración, donde puede ayudar a aislar y probar componentes específicos de un sistema más grande. Sin embargo, es importante usar el parcheo con prudencia, ya que el uso excesivo o incorrecto de esta técnica puede conducir a un código complejo y frágil que es difícil de mantener y depurar. Como con cualquier herramienta o técnica en el desarrollo de software, es importante sopesar los beneficios y las desventajas del parcheo y usarlo de manera apropiada para la situación específica en cuestión.

Aquí tienes un ejemplo de parcheo:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

En esta prueba, estamos parcheando mi_metodo para que siempre devuelva 3, similar al ejemplo anterior. Sin embargo, esta vez estamos parcheando el método durante toda la duración del bloque with. Cualquier código dentro del bloque with que llame a mi_metodo utilizará el objeto simulado en lugar del método real. Después del bloque with, se restaura el método original.

La función patch también devuelve un objeto simulado en el que podemos realizar afirmaciones. En este caso, estamos afirmando que el método fue llamado exactamente una vez.

La simulación y el parcheo son herramientas poderosas que nos permiten escribir pruebas para nuestro código de forma aislada, lo que conduce a pruebas más rápidas y confiables. Son herramientas esenciales para el conjunto de herramientas de pruebas de cualquier desarrollador de Python.

11.2.1 Simulación y Efectos Secundarios

Simulación

En Python, un Mock es una herramienta poderosa que puede ayudarte a probar tu código de manera más exhaustiva. Un objeto Mock puede sustituir a otro objeto en tu sistema, lo que te permite aislar partes de tu código y asegurarte de que funcionen correctamente.

Controlando cómo se comporta el Mock (como especificando sus valores de retorno o efectos secundarios cuando se llaman sus métodos), puedes simular una amplia gama de escenarios y asegurarte de que tu código los maneje correctamente. Esto puede ayudarte a detectar errores que de otro modo podrían pasar desapercibidos.

Además, al hacer afirmaciones sobre cómo se usó el Mock, puedes verificar que tu código esté interactuando con otras partes de tu sistema de la manera que esperas. Todo esto puede sumar más confianza en tu código y menos errores en producción.

Ejemplo:

Aquí tienes un ejemplo simple de un objeto simulado en acción:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Efectos Secundarios

Otra característica de los objetos simulados es que puedes configurarlos para hacer más que simplemente imitar el comportamiento del objeto real. Por ejemplo, puedes configurar un objeto simulado para que lance una excepción cuando se llama, o para que devuelva diferentes valores cada vez que se llama.

Esto se llama establecer un efecto secundario para el simulado. Al usar efectos secundarios, puedes probar a fondo cómo maneja tu código diferentes escenarios y casos extremos. Además, puedes usar simulaciones para simular diferentes entornos, como una conexión de red lenta o una base de datos que está fuera de línea.

Esto te permite probar cómo se comporta tu código en una variedad de situaciones, asegurando que sea sólido y confiable.

Ejemplo:

Aquí tienes un ejemplo de configuración de un efecto secundario:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

En este ejemplo, cada llamada al objeto simulado devuelve el siguiente valor de la lista que especificamos.

Simulación de Métodos y Atributos

Un caso de uso importante para los objetos simulados es actuar como métodos o atributos en tus objetos. Además del ejemplo dado en el texto original, considera lo siguiente: podrías tener un objeto que depende de un archivo o fuente de datos particular para realizar su función.

Al simular el archivo o fuente de datos, puedes probar el comportamiento de tu objeto sin depender de recursos externos. Alternativamente, podrías usar un objeto simulado para simular una cierta condición, como un nivel bajo de batería o una conexión de red deficiente, para asegurarte de que tu objeto maneje estos escenarios de manera elegante.

La flexibilidad de los objetos simulados los convierte en una herramienta poderosa para probar y garantizar la robustez de tu código.

Ejemplo:

Aquí tienes un ejemplo de cómo simular un método:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

En este ejemplo, hemos reemplazado el método my_method en MyObject con un objeto simulado, por lo que ahora llamar a my_method devuelve el valor simulado en lugar del valor original.

Recuerda que la simulación y el parcheo son solo herramientas para aislar tu código para pruebas unitarias. Deben usarse con moderación y prudencia, ya que su uso excesivo puede llevar a pruebas difíciles de entender y mantener. Pero cuando se utilizan adecuadamente, pueden hacer que tus pruebas sean más confiables, rápidas y fáciles de escribir.

11.2.2 PyTest

PyTest es un marco de pruebas que permite escribir pruebas de manera más pythonica, lo que significa que podemos escribir casos de prueba de una manera similar a la escritura de scripts de Python ordinarios. Esto facilita a los desarrolladores escribir pruebas, ya que no tienen que aprender un nuevo lenguaje solo para escribir pruebas.

Además, PyTest simplifica el proceso de construir escenarios de prueba funcional complejos, permitiendo a los desarrolladores concentrarse en escribir pruebas que reflejen con precisión la funcionalidad del código que están probando. PyTest también es conocido por su riqueza de funciones y simplicidad de uso, lo que lo convierte en una opción popular entre los desarrolladores de todos los niveles de habilidad.

Además, PyTest es altamente extensible, con una amplia gama de complementos disponibles que se pueden utilizar para ampliar aún más su funcionalidad. En general, PyTest es un marco de pruebas versátil y potente que puede simplificar en gran medida el proceso de pruebas para los desarrolladores, al tiempo que proporciona una amplia gama de características y opciones de personalización para garantizar que las pruebas sean precisas y efectivas.

Ejemplo:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

Puedes ejecutar la prueba con el comando pytest. Esto buscará archivos que comiencen con test_ o terminen con _test y ejecutará cualquier función que comience con test_.

11.2 Simulación y parcheo

Simulación es una técnica esencial en las pruebas de software donde reemplazas partes de tu sistema con objetos simulados y haces afirmaciones sobre cómo se usaron. Este enfoque te permite simular el comportamiento de tu sistema sin involucrar todas sus componentes, lo que puede ser lento e ineficiente.

Para implementar la simulación, puedes usar unittest.mock, una biblioteca para pruebas en Python. Esta biblioteca proporciona un conjunto extenso de herramientas para crear y usar objetos simulados con facilidad, lo que te permite reemplazar partes de tu sistema en prueba y hacer afirmaciones sobre cómo se han utilizado.

Un objeto simulado es un objeto ficticio y flexible que actúa como sustituto de un objeto real. Devuelve a sí mismo cada vez que llamas a cualquier método o accedes a cualquier atributo, y registra qué métodos se llamaron y cuáles fueron los parámetros. Esto lo convierte en una excelente herramienta para simular comportamientos complejos y probar casos límite en tu código, lo que te permite detectar errores temprano en el proceso de desarrollo.

Ejemplo:

Aquí tienes un ejemplo sencillo para mostrar cómo podrías usar la simulación:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Parcheo es una técnica comúnmente utilizada en el desarrollo de software, especialmente en pruebas unitarias. Permite a los desarrolladores reemplazar un método o un atributo en un módulo o una clase con un nuevo objeto, lo que puede ser particularmente útil al probar código que depende de sistemas externos o recursos que pueden no estar disponibles o ser poco confiables. Al reemplazar estas dependencias con objetos simulados, los desarrolladores pueden simular el comportamiento del sistema externo o recurso, lo que les permite probar su código de manera aislada y detectar posibles problemas temprano.

Un aspecto importante a tener en cuenta al parchear es asegurarse de que el nuevo objeto que se está utilizando como reemplazo imite correctamente el comportamiento del objeto original que se está reemplazando. Esto a menudo implica crear un objeto simulado personalizado que implemente la misma interfaz o herede de la misma clase base que el objeto original, y luego anular o simular los métodos o atributos relevantes.

Además de las pruebas unitarias, el parcheo también se puede utilizar en otras áreas del desarrollo de software, como las pruebas de integración, donde puede ayudar a aislar y probar componentes específicos de un sistema más grande. Sin embargo, es importante usar el parcheo con prudencia, ya que el uso excesivo o incorrecto de esta técnica puede conducir a un código complejo y frágil que es difícil de mantener y depurar. Como con cualquier herramienta o técnica en el desarrollo de software, es importante sopesar los beneficios y las desventajas del parcheo y usarlo de manera apropiada para la situación específica en cuestión.

Aquí tienes un ejemplo de parcheo:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

En esta prueba, estamos parcheando mi_metodo para que siempre devuelva 3, similar al ejemplo anterior. Sin embargo, esta vez estamos parcheando el método durante toda la duración del bloque with. Cualquier código dentro del bloque with que llame a mi_metodo utilizará el objeto simulado en lugar del método real. Después del bloque with, se restaura el método original.

La función patch también devuelve un objeto simulado en el que podemos realizar afirmaciones. En este caso, estamos afirmando que el método fue llamado exactamente una vez.

La simulación y el parcheo son herramientas poderosas que nos permiten escribir pruebas para nuestro código de forma aislada, lo que conduce a pruebas más rápidas y confiables. Son herramientas esenciales para el conjunto de herramientas de pruebas de cualquier desarrollador de Python.

11.2.1 Simulación y Efectos Secundarios

Simulación

En Python, un Mock es una herramienta poderosa que puede ayudarte a probar tu código de manera más exhaustiva. Un objeto Mock puede sustituir a otro objeto en tu sistema, lo que te permite aislar partes de tu código y asegurarte de que funcionen correctamente.

Controlando cómo se comporta el Mock (como especificando sus valores de retorno o efectos secundarios cuando se llaman sus métodos), puedes simular una amplia gama de escenarios y asegurarte de que tu código los maneje correctamente. Esto puede ayudarte a detectar errores que de otro modo podrían pasar desapercibidos.

Además, al hacer afirmaciones sobre cómo se usó el Mock, puedes verificar que tu código esté interactuando con otras partes de tu sistema de la manera que esperas. Todo esto puede sumar más confianza en tu código y menos errores en producción.

Ejemplo:

Aquí tienes un ejemplo simple de un objeto simulado en acción:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Efectos Secundarios

Otra característica de los objetos simulados es que puedes configurarlos para hacer más que simplemente imitar el comportamiento del objeto real. Por ejemplo, puedes configurar un objeto simulado para que lance una excepción cuando se llama, o para que devuelva diferentes valores cada vez que se llama.

Esto se llama establecer un efecto secundario para el simulado. Al usar efectos secundarios, puedes probar a fondo cómo maneja tu código diferentes escenarios y casos extremos. Además, puedes usar simulaciones para simular diferentes entornos, como una conexión de red lenta o una base de datos que está fuera de línea.

Esto te permite probar cómo se comporta tu código en una variedad de situaciones, asegurando que sea sólido y confiable.

Ejemplo:

Aquí tienes un ejemplo de configuración de un efecto secundario:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

En este ejemplo, cada llamada al objeto simulado devuelve el siguiente valor de la lista que especificamos.

Simulación de Métodos y Atributos

Un caso de uso importante para los objetos simulados es actuar como métodos o atributos en tus objetos. Además del ejemplo dado en el texto original, considera lo siguiente: podrías tener un objeto que depende de un archivo o fuente de datos particular para realizar su función.

Al simular el archivo o fuente de datos, puedes probar el comportamiento de tu objeto sin depender de recursos externos. Alternativamente, podrías usar un objeto simulado para simular una cierta condición, como un nivel bajo de batería o una conexión de red deficiente, para asegurarte de que tu objeto maneje estos escenarios de manera elegante.

La flexibilidad de los objetos simulados los convierte en una herramienta poderosa para probar y garantizar la robustez de tu código.

Ejemplo:

Aquí tienes un ejemplo de cómo simular un método:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

En este ejemplo, hemos reemplazado el método my_method en MyObject con un objeto simulado, por lo que ahora llamar a my_method devuelve el valor simulado en lugar del valor original.

Recuerda que la simulación y el parcheo son solo herramientas para aislar tu código para pruebas unitarias. Deben usarse con moderación y prudencia, ya que su uso excesivo puede llevar a pruebas difíciles de entender y mantener. Pero cuando se utilizan adecuadamente, pueden hacer que tus pruebas sean más confiables, rápidas y fáciles de escribir.

11.2.2 PyTest

PyTest es un marco de pruebas que permite escribir pruebas de manera más pythonica, lo que significa que podemos escribir casos de prueba de una manera similar a la escritura de scripts de Python ordinarios. Esto facilita a los desarrolladores escribir pruebas, ya que no tienen que aprender un nuevo lenguaje solo para escribir pruebas.

Además, PyTest simplifica el proceso de construir escenarios de prueba funcional complejos, permitiendo a los desarrolladores concentrarse en escribir pruebas que reflejen con precisión la funcionalidad del código que están probando. PyTest también es conocido por su riqueza de funciones y simplicidad de uso, lo que lo convierte en una opción popular entre los desarrolladores de todos los niveles de habilidad.

Además, PyTest es altamente extensible, con una amplia gama de complementos disponibles que se pueden utilizar para ampliar aún más su funcionalidad. En general, PyTest es un marco de pruebas versátil y potente que puede simplificar en gran medida el proceso de pruebas para los desarrolladores, al tiempo que proporciona una amplia gama de características y opciones de personalización para garantizar que las pruebas sean precisas y efectivas.

Ejemplo:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

Puedes ejecutar la prueba con el comando pytest. Esto buscará archivos que comiencen con test_ o terminen con _test y ejecutará cualquier función que comience con test_.

11.2 Simulación y parcheo

Simulación es una técnica esencial en las pruebas de software donde reemplazas partes de tu sistema con objetos simulados y haces afirmaciones sobre cómo se usaron. Este enfoque te permite simular el comportamiento de tu sistema sin involucrar todas sus componentes, lo que puede ser lento e ineficiente.

Para implementar la simulación, puedes usar unittest.mock, una biblioteca para pruebas en Python. Esta biblioteca proporciona un conjunto extenso de herramientas para crear y usar objetos simulados con facilidad, lo que te permite reemplazar partes de tu sistema en prueba y hacer afirmaciones sobre cómo se han utilizado.

Un objeto simulado es un objeto ficticio y flexible que actúa como sustituto de un objeto real. Devuelve a sí mismo cada vez que llamas a cualquier método o accedes a cualquier atributo, y registra qué métodos se llamaron y cuáles fueron los parámetros. Esto lo convierte en una excelente herramienta para simular comportamientos complejos y probar casos límite en tu código, lo que te permite detectar errores temprano en el proceso de desarrollo.

Ejemplo:

Aquí tienes un ejemplo sencillo para mostrar cómo podrías usar la simulación:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Parcheo es una técnica comúnmente utilizada en el desarrollo de software, especialmente en pruebas unitarias. Permite a los desarrolladores reemplazar un método o un atributo en un módulo o una clase con un nuevo objeto, lo que puede ser particularmente útil al probar código que depende de sistemas externos o recursos que pueden no estar disponibles o ser poco confiables. Al reemplazar estas dependencias con objetos simulados, los desarrolladores pueden simular el comportamiento del sistema externo o recurso, lo que les permite probar su código de manera aislada y detectar posibles problemas temprano.

Un aspecto importante a tener en cuenta al parchear es asegurarse de que el nuevo objeto que se está utilizando como reemplazo imite correctamente el comportamiento del objeto original que se está reemplazando. Esto a menudo implica crear un objeto simulado personalizado que implemente la misma interfaz o herede de la misma clase base que el objeto original, y luego anular o simular los métodos o atributos relevantes.

Además de las pruebas unitarias, el parcheo también se puede utilizar en otras áreas del desarrollo de software, como las pruebas de integración, donde puede ayudar a aislar y probar componentes específicos de un sistema más grande. Sin embargo, es importante usar el parcheo con prudencia, ya que el uso excesivo o incorrecto de esta técnica puede conducir a un código complejo y frágil que es difícil de mantener y depurar. Como con cualquier herramienta o técnica en el desarrollo de software, es importante sopesar los beneficios y las desventajas del parcheo y usarlo de manera apropiada para la situación específica en cuestión.

Aquí tienes un ejemplo de parcheo:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

En esta prueba, estamos parcheando mi_metodo para que siempre devuelva 3, similar al ejemplo anterior. Sin embargo, esta vez estamos parcheando el método durante toda la duración del bloque with. Cualquier código dentro del bloque with que llame a mi_metodo utilizará el objeto simulado en lugar del método real. Después del bloque with, se restaura el método original.

La función patch también devuelve un objeto simulado en el que podemos realizar afirmaciones. En este caso, estamos afirmando que el método fue llamado exactamente una vez.

La simulación y el parcheo son herramientas poderosas que nos permiten escribir pruebas para nuestro código de forma aislada, lo que conduce a pruebas más rápidas y confiables. Son herramientas esenciales para el conjunto de herramientas de pruebas de cualquier desarrollador de Python.

11.2.1 Simulación y Efectos Secundarios

Simulación

En Python, un Mock es una herramienta poderosa que puede ayudarte a probar tu código de manera más exhaustiva. Un objeto Mock puede sustituir a otro objeto en tu sistema, lo que te permite aislar partes de tu código y asegurarte de que funcionen correctamente.

Controlando cómo se comporta el Mock (como especificando sus valores de retorno o efectos secundarios cuando se llaman sus métodos), puedes simular una amplia gama de escenarios y asegurarte de que tu código los maneje correctamente. Esto puede ayudarte a detectar errores que de otro modo podrían pasar desapercibidos.

Además, al hacer afirmaciones sobre cómo se usó el Mock, puedes verificar que tu código esté interactuando con otras partes de tu sistema de la manera que esperas. Todo esto puede sumar más confianza en tu código y menos errores en producción.

Ejemplo:

Aquí tienes un ejemplo simple de un objeto simulado en acción:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Efectos Secundarios

Otra característica de los objetos simulados es que puedes configurarlos para hacer más que simplemente imitar el comportamiento del objeto real. Por ejemplo, puedes configurar un objeto simulado para que lance una excepción cuando se llama, o para que devuelva diferentes valores cada vez que se llama.

Esto se llama establecer un efecto secundario para el simulado. Al usar efectos secundarios, puedes probar a fondo cómo maneja tu código diferentes escenarios y casos extremos. Además, puedes usar simulaciones para simular diferentes entornos, como una conexión de red lenta o una base de datos que está fuera de línea.

Esto te permite probar cómo se comporta tu código en una variedad de situaciones, asegurando que sea sólido y confiable.

Ejemplo:

Aquí tienes un ejemplo de configuración de un efecto secundario:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

En este ejemplo, cada llamada al objeto simulado devuelve el siguiente valor de la lista que especificamos.

Simulación de Métodos y Atributos

Un caso de uso importante para los objetos simulados es actuar como métodos o atributos en tus objetos. Además del ejemplo dado en el texto original, considera lo siguiente: podrías tener un objeto que depende de un archivo o fuente de datos particular para realizar su función.

Al simular el archivo o fuente de datos, puedes probar el comportamiento de tu objeto sin depender de recursos externos. Alternativamente, podrías usar un objeto simulado para simular una cierta condición, como un nivel bajo de batería o una conexión de red deficiente, para asegurarte de que tu objeto maneje estos escenarios de manera elegante.

La flexibilidad de los objetos simulados los convierte en una herramienta poderosa para probar y garantizar la robustez de tu código.

Ejemplo:

Aquí tienes un ejemplo de cómo simular un método:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

En este ejemplo, hemos reemplazado el método my_method en MyObject con un objeto simulado, por lo que ahora llamar a my_method devuelve el valor simulado en lugar del valor original.

Recuerda que la simulación y el parcheo son solo herramientas para aislar tu código para pruebas unitarias. Deben usarse con moderación y prudencia, ya que su uso excesivo puede llevar a pruebas difíciles de entender y mantener. Pero cuando se utilizan adecuadamente, pueden hacer que tus pruebas sean más confiables, rápidas y fáciles de escribir.

11.2.2 PyTest

PyTest es un marco de pruebas que permite escribir pruebas de manera más pythonica, lo que significa que podemos escribir casos de prueba de una manera similar a la escritura de scripts de Python ordinarios. Esto facilita a los desarrolladores escribir pruebas, ya que no tienen que aprender un nuevo lenguaje solo para escribir pruebas.

Además, PyTest simplifica el proceso de construir escenarios de prueba funcional complejos, permitiendo a los desarrolladores concentrarse en escribir pruebas que reflejen con precisión la funcionalidad del código que están probando. PyTest también es conocido por su riqueza de funciones y simplicidad de uso, lo que lo convierte en una opción popular entre los desarrolladores de todos los niveles de habilidad.

Además, PyTest es altamente extensible, con una amplia gama de complementos disponibles que se pueden utilizar para ampliar aún más su funcionalidad. En general, PyTest es un marco de pruebas versátil y potente que puede simplificar en gran medida el proceso de pruebas para los desarrolladores, al tiempo que proporciona una amplia gama de características y opciones de personalización para garantizar que las pruebas sean precisas y efectivas.

Ejemplo:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

Puedes ejecutar la prueba con el comando pytest. Esto buscará archivos que comiencen con test_ o terminen con _test y ejecutará cualquier función que comience con test_.

11.2 Simulación y parcheo

Simulación es una técnica esencial en las pruebas de software donde reemplazas partes de tu sistema con objetos simulados y haces afirmaciones sobre cómo se usaron. Este enfoque te permite simular el comportamiento de tu sistema sin involucrar todas sus componentes, lo que puede ser lento e ineficiente.

Para implementar la simulación, puedes usar unittest.mock, una biblioteca para pruebas en Python. Esta biblioteca proporciona un conjunto extenso de herramientas para crear y usar objetos simulados con facilidad, lo que te permite reemplazar partes de tu sistema en prueba y hacer afirmaciones sobre cómo se han utilizado.

Un objeto simulado es un objeto ficticio y flexible que actúa como sustituto de un objeto real. Devuelve a sí mismo cada vez que llamas a cualquier método o accedes a cualquier atributo, y registra qué métodos se llamaron y cuáles fueron los parámetros. Esto lo convierte en una excelente herramienta para simular comportamientos complejos y probar casos límite en tu código, lo que te permite detectar errores temprano en el proceso de desarrollo.

Ejemplo:

Aquí tienes un ejemplo sencillo para mostrar cómo podrías usar la simulación:

from unittest.mock import Mock

# Create a Mock object
mock = Mock()

# Use the mock
mock.some_method(1, 2, 3)

# Make an assertion about how the mock was used
mock.some_method.assert_called_once_with(1, 2, 3)

Parcheo es una técnica comúnmente utilizada en el desarrollo de software, especialmente en pruebas unitarias. Permite a los desarrolladores reemplazar un método o un atributo en un módulo o una clase con un nuevo objeto, lo que puede ser particularmente útil al probar código que depende de sistemas externos o recursos que pueden no estar disponibles o ser poco confiables. Al reemplazar estas dependencias con objetos simulados, los desarrolladores pueden simular el comportamiento del sistema externo o recurso, lo que les permite probar su código de manera aislada y detectar posibles problemas temprano.

Un aspecto importante a tener en cuenta al parchear es asegurarse de que el nuevo objeto que se está utilizando como reemplazo imite correctamente el comportamiento del objeto original que se está reemplazando. Esto a menudo implica crear un objeto simulado personalizado que implemente la misma interfaz o herede de la misma clase base que el objeto original, y luego anular o simular los métodos o atributos relevantes.

Además de las pruebas unitarias, el parcheo también se puede utilizar en otras áreas del desarrollo de software, como las pruebas de integración, donde puede ayudar a aislar y probar componentes específicos de un sistema más grande. Sin embargo, es importante usar el parcheo con prudencia, ya que el uso excesivo o incorrecto de esta técnica puede conducir a un código complejo y frágil que es difícil de mantener y depurar. Como con cualquier herramienta o técnica en el desarrollo de software, es importante sopesar los beneficios y las desventajas del parcheo y usarlo de manera apropiada para la situación específica en cuestión.

Aquí tienes un ejemplo de parcheo:

pythonCopy code
from unittest.mock import patch

def test_my_function():
    with patch('my_module.MyObject.my_method', return_value=3) as mock_method:
        assert my_function(MyObject()) == 3
    mock_method.assert_called_once()

En esta prueba, estamos parcheando mi_metodo para que siempre devuelva 3, similar al ejemplo anterior. Sin embargo, esta vez estamos parcheando el método durante toda la duración del bloque with. Cualquier código dentro del bloque with que llame a mi_metodo utilizará el objeto simulado en lugar del método real. Después del bloque with, se restaura el método original.

La función patch también devuelve un objeto simulado en el que podemos realizar afirmaciones. En este caso, estamos afirmando que el método fue llamado exactamente una vez.

La simulación y el parcheo son herramientas poderosas que nos permiten escribir pruebas para nuestro código de forma aislada, lo que conduce a pruebas más rápidas y confiables. Son herramientas esenciales para el conjunto de herramientas de pruebas de cualquier desarrollador de Python.

11.2.1 Simulación y Efectos Secundarios

Simulación

En Python, un Mock es una herramienta poderosa que puede ayudarte a probar tu código de manera más exhaustiva. Un objeto Mock puede sustituir a otro objeto en tu sistema, lo que te permite aislar partes de tu código y asegurarte de que funcionen correctamente.

Controlando cómo se comporta el Mock (como especificando sus valores de retorno o efectos secundarios cuando se llaman sus métodos), puedes simular una amplia gama de escenarios y asegurarte de que tu código los maneje correctamente. Esto puede ayudarte a detectar errores que de otro modo podrían pasar desapercibidos.

Además, al hacer afirmaciones sobre cómo se usó el Mock, puedes verificar que tu código esté interactuando con otras partes de tu sistema de la manera que esperas. Todo esto puede sumar más confianza en tu código y menos errores en producción.

Ejemplo:

Aquí tienes un ejemplo simple de un objeto simulado en acción:

pythonCopy code
from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.return_value = 'hello world'

# Use the mock object
result = mock()

# Check the result
print(result)  # prints: hello world

# Check if the mock was called
print(mock.called)  # prints: True

Efectos Secundarios

Otra característica de los objetos simulados es que puedes configurarlos para hacer más que simplemente imitar el comportamiento del objeto real. Por ejemplo, puedes configurar un objeto simulado para que lance una excepción cuando se llama, o para que devuelva diferentes valores cada vez que se llama.

Esto se llama establecer un efecto secundario para el simulado. Al usar efectos secundarios, puedes probar a fondo cómo maneja tu código diferentes escenarios y casos extremos. Además, puedes usar simulaciones para simular diferentes entornos, como una conexión de red lenta o una base de datos que está fuera de línea.

Esto te permite probar cómo se comporta tu código en una variedad de situaciones, asegurando que sea sólido y confiable.

Ejemplo:

Aquí tienes un ejemplo de configuración de un efecto secundario:

from unittest.mock import Mock

# Create a mock object
mock = Mock()
mock.side_effect = [1, 2, 3, 4, 5]

# Use the mock object
print(mock())  # prints: 1
print(mock())  # prints: 2
print(mock())  # prints: 3

En este ejemplo, cada llamada al objeto simulado devuelve el siguiente valor de la lista que especificamos.

Simulación de Métodos y Atributos

Un caso de uso importante para los objetos simulados es actuar como métodos o atributos en tus objetos. Además del ejemplo dado en el texto original, considera lo siguiente: podrías tener un objeto que depende de un archivo o fuente de datos particular para realizar su función.

Al simular el archivo o fuente de datos, puedes probar el comportamiento de tu objeto sin depender de recursos externos. Alternativamente, podrías usar un objeto simulado para simular una cierta condición, como un nivel bajo de batería o una conexión de red deficiente, para asegurarte de que tu objeto maneje estos escenarios de manera elegante.

La flexibilidad de los objetos simulados los convierte en una herramienta poderosa para probar y garantizar la robustez de tu código.

Ejemplo:

Aquí tienes un ejemplo de cómo simular un método:

from unittest.mock import Mock

class MyObject:
    def my_method(self):
        return 'original value'

# Replace my_method with a mock
MyObject.my_method = Mock(return_value='mocked value')

obj = MyObject()
print(obj.my_method())  # prints: mocked value

En este ejemplo, hemos reemplazado el método my_method en MyObject con un objeto simulado, por lo que ahora llamar a my_method devuelve el valor simulado en lugar del valor original.

Recuerda que la simulación y el parcheo son solo herramientas para aislar tu código para pruebas unitarias. Deben usarse con moderación y prudencia, ya que su uso excesivo puede llevar a pruebas difíciles de entender y mantener. Pero cuando se utilizan adecuadamente, pueden hacer que tus pruebas sean más confiables, rápidas y fáciles de escribir.

11.2.2 PyTest

PyTest es un marco de pruebas que permite escribir pruebas de manera más pythonica, lo que significa que podemos escribir casos de prueba de una manera similar a la escritura de scripts de Python ordinarios. Esto facilita a los desarrolladores escribir pruebas, ya que no tienen que aprender un nuevo lenguaje solo para escribir pruebas.

Además, PyTest simplifica el proceso de construir escenarios de prueba funcional complejos, permitiendo a los desarrolladores concentrarse en escribir pruebas que reflejen con precisión la funcionalidad del código que están probando. PyTest también es conocido por su riqueza de funciones y simplicidad de uso, lo que lo convierte en una opción popular entre los desarrolladores de todos los niveles de habilidad.

Además, PyTest es altamente extensible, con una amplia gama de complementos disponibles que se pueden utilizar para ampliar aún más su funcionalidad. En general, PyTest es un marco de pruebas versátil y potente que puede simplificar en gran medida el proceso de pruebas para los desarrolladores, al tiempo que proporciona una amplia gama de características y opciones de personalización para garantizar que las pruebas sean precisas y efectivas.

Ejemplo:

import pytest

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add('space', 'ship') == 'spaceship'

Puedes ejecutar la prueba con el comando pytest. Esto buscará archivos que comiencen con test_ o terminen con _test y ejecutará cualquier función que comience con test_.