Pruebas unitarias en Python

pruebas unitarias en python en la práctica

Posted by murilobsd July 2, 2018

You may find interesting:


Compilación e instalacion Python3 en Debian 8 y 9

desde un sistema base

Write Test --> Test Fails --> Write Code --> Test Passes --> Refactor <--> Write Teste ....

¿Qué son las pruebas unitarias?

Una prueba unitaria básicamente es la prueba de la menor parte probable de un programa.

Pero, ¿qué quiere decir?

Si programa en un lenguaje que admite paradigma funcional, por ejemplo, la parte más baja de su código debe ser una función. Entonces una prueba unitaria sería la prueba de cualquier función. En el caso de orientación a objetos sería la prueba de un método de su objeto.

Python tiene una biblioteca incorporada para pruebas unitarias unittest. El objetivo de este post será crear una función para comprobar si hay dígito 0,1,2,3,4,5,6,7,9 en una determinada frase, en caso afirmativo retorna verdadero si no falso.

Iniciaremos la creación de un archivo con el prefijo de test

 
$touch test_comprobar_digito.py 

En el archivo vamos a crear una clase que hereda de TestCase, en la que tendremos varios métodos listos para facilitar en los casos, vea que los métodos necesitan tener el prefijo de test.

# coding:utf-8

import unittest

class TestCheckDigit(unittest.TestCase):

    def test_comprobar_digito_ok(self):
        pass

En el método anterior utilizamos el estándar Arrange-Act-Assert, en el cual primero definimos los inputs después actuamos en el método y por fin hacemos la aserción.

# coding:utf-8

import unittest

class TestCheckDigit(unittest.TestCase):

    def test_comprobar_digito_ok(self):
        # input
        sentencia = "hoy es el 2 de julio de 2018, Brasil ganó 2x0 de México en la copa del mundo."

        # act
        hay_digito = comprobar_digito(sentencia)

        # assert
        self.assertTrue(existe_digito)

La clase TestCase proporciona varios métodos para aserciones

Para ejecutar la prueba anterior, basta con escribir el comando abajo:

 
$python -m unittest test_comprobar_digito

Tendremos una salida parecida a la siguiente:

E
======================================================================
ERROR: test_comprobar_digito_ok (test_comprobar_digito.TestCheckDigit)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_comprobar_digito.py", line 12, in test_comprobar_digito_ok
    hay_digito = comprobar_digito(sentencia)
NameError: global name 'comprobar_digito' is not defined

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

Op’s pero no importamos ni creamos la función de buscar_digito, por eso que ha devuelto un error. Vamos a arreglar el error.

Crear un archivo:

 
$touch comprobar.py 

Dentro de ese archivo creamos la función:

# coding:utf-8

def comprobar_digito():
    pass

Ahora importaremos la función dentro de nuestra prueba.

# coding:utf-8

import unittest

from comprobar import comprobar_digito

class TestCheckDigit(unittest.TestCase):

    def test_comprobar_digito_ok(self):
        # input
        sentencia = "hoy es el 2 de julio de 2018, Brasil ganó 2x0 de México en la copa del mundo."

        # act
        hay_digito = comprobar_digito(sentencia)

        # assert
        self.assertTrue(existe_digito)

De nuevo, ejecute el comando para ejecutar las pruebas:

 
$python -m unittest test_comprobar_digito

Op’s más un error, deje que la prueba nos guíe !!!

E
======================================================================
ERROR: test_comprobar_digito_ok (test_comprobar_digito.TestCheckDigit)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_comprobar_digito.py", line 14, in test_comprobar_digito_ok
    hay_digito = comprobar_digito(sentencia)
TypeError: comprobar_digito() takes no arguments (1 given)

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

Necesitamos recibir un argumento en nuestra función:

# coding:utf-8

def comprobar_digito(sentencia):
    pass
F
======================================================================
FAIL: test_comprobar_digito_ok (test_comprobar_digito.TestCheckDigit)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_comprobar_digito.py", line 17, in test_comprobar_digito_ok
    self.assertTrue(hay_digito)
AssertionError: None is not true

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

Y la salida mejoró, ahora está volviendo una falta, entonces concertamos todos los errores de semántica del código ahora realmente la aserción está fallando.

# coding:utf-8

def comprobar_digito(sentencia):
    return True
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Perfecto ahora la prueba ha pasado ;)

¡Incluso la prueba pasando quedó aquella duda! Pero es que mi función está teniendo el comportamiento correcto !? Bueno para eso haremos un nuevo. prueba.

# coding:utf-8

import unittest

from comprobar import comprobar_digito

class TestCheckDigit(unittest.TestCase):

    def test_comprobar_digito_ok(self):
        # input
        sentencia = "hoy es el 2 de julio de 2018, Brasil ganó 2x0 de México en la copa del mundo."

        # act
        hay_digito = comprobar_digito(sentencia)

        # assert
        self.assertTrue(hay_digito)
    
    def test_comprobar_digito_error(self):
        # input
        sentencia = "Chi chi chi le le le."

        # act
        hay_digito = comprobar_digito(sentencia)

        # assert
        self.assertFalse(hay_digito)

De nuevo ejecutamos las pruebas para ver si todo está bien.

F.
======================================================================
FAIL: test_comprobar_digito_error (test_comprobar_digito.TestCheckDigit)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_comprobar_digito.py", line 27, in test_comprobar_digito_error
    self.assertFalse(hay_digito)
AssertionError: True is not false

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)

¡Que bueno! Nuestra función realmente no está teniendo el comportamiento previsto, vamos a refactorizarla.

# coding:utf-8

def comprobar_digito(sentencia):
    for carta in sentencia:
        if carta.isdigit():
            return True
    return False

Realizamos nuevamente las pruebas:

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Perfecto, tenemos una función que cheque si hay dígito o no en una determinada frase, cubierta de pruebas y creada con TDD.