Requisito previo: prueba de mutación
Mutpy es una herramienta de prueba de mutación en Python que genera mutantes y calcula una puntuación de mutación. Es compatible con el módulo de prueba unitaria estándar, genera informes YAML/HTML y tiene una salida colorida. Aplica mutación a nivel de AST.
Instalación:
Este módulo no viene integrado con Python. Para instalarlo, escriba el siguiente comando en la terminal.
pip install mutpy
Aquí, usaremos la biblioteca Mutpy para ejecutar casos de prueba escritos para un programa simple.
Ahora, realizamos pruebas de mutación para un programa que verifica si un número es primo o no .
Código: Este código se guarda como isPrime.py
Python3
# define a function def isPrime(num): if num > 1: # check for factors for i in range(2,num): if (num % i) == 0: return False else: return True # if input number is less than # or equal to 1, it is not prime else: return False;
Ahora, necesitamos escribir casos de prueba para el programa anterior usando la biblioteca Pytest o Unittest . Los casos de prueba deben escribirse con un enfoque para matar a todos los mutantes, es decir, los casos de prueba deben ser lo suficientemente efectivos para dar una buena puntuación de mutación. Los casos de prueba se escriben utilizando la biblioteca Unittest en el archivo que se muestra a continuación.
Los casos de prueba se escriben utilizando self.assertEqual(), que es una aserción de prueba que generalmente se usa para determinar si un caso de prueba ha pasado o no. Hemos escrito tres funciones de prueba a continuación para verificar los tres tipos de entrada:
- La entrada es principal
- La entrada no es prima
- La entrada no es válida
Nota: El nombre de la función y el nombre del archivo de prueba siempre deben comenzar con la palabra ‘prueba’.
Código: este código se guarda como test_isPrime.py.
Python3
# import required libraries from unittest import TestCase from isPrime import isPrime # define a class class CalculatorTest(TestCase): # test case for checking non prime nums def test_nonprime(self): self.assertEqual(isPrime(12),False) # test case to check prime nums def test_prime(self): self.assertEqual(isPrime(19),True) # test case to check invalid input def test_invalid(self): self.assertEqual(isPrime(-1),False)
Para ejecutar estos casos de prueba, necesitamos crear dos archivos separados isPrime.py y test_isPrime.py en una sola carpeta y ejecutar el siguiente comando en el símbolo del sistema:
mut.py --target isPrime --unit-test test_isPrime -m --runner pytest
En el comando anterior, tenemos que especificar tres cosas:
- Destino: el archivo de destino en el que se ejecutarán los casos de prueba, que en nuestro caso es isPrime.py
- Unit-test: el archivo que contiene las pruebas unitarias que deben ejecutarse, es decir, test_isPrime.py en nuestro caso.
- Corredor: pytest o unittest
El resultado será un conjunto de mutantes junto con detalles como el puntaje de mutación, la cantidad de mutaciones muertas, sobrevivientes, etc.
[*] Start mutation process: - targets: isPrime - tests: test_isPrime [*] 3 tests passed: - test_isPrime [3.07469 s] [*] Start mutants generation and execution: - [# 1] AOR isPrime: -------------------------------------------------------------------------------- 3: 4: if num > 1: 5: 6: for i in range(2, num): - 7: if num % i == 0: + 7: if num * i == 0: 8: return False 9: else: 10: 11: return True -------------------------------------------------------------------------------- [1.45151 s] killed by testing program mutpy/test_isPrime.py::CalculatorTest::test_nonprime - [# 2] COI isPrime: -------------------------------------------------------------------------------- 1: 2: def isPrime(num): 3: - 4: if num > 1: + 4: if not (num > 1): 5: 6: for i in range(2, num): 7: if num % i == 0: 8: return False -------------------------------------------------------------------------------- [1.25723 s] killed by testing program mutpy/test_isPrime.py::CalculatorTest::test_invalid - [# 3] COI isPrime: -------------------------------------------------------------------------------- 3: 4: if num > 1: 5: 6: for i in range(2, num): - 7: if num % i == 0: + 7: if not (num % i == 0): 8: return False 9: else: 10: 11: return True -------------------------------------------------------------------------------- [1.28817 s] killed by testing program mutpy/test_isPrime.py::CalculatorTest::test_prime - [# 4] CRP isPrime: -------------------------------------------------------------------------------- 1: 2: def isPrime(num): 3: - 4: if num > 1: + 4: if num > 2: 5: 6: for i in range(2, num): 7: if num % i == 0: 8: return False -------------------------------------------------------------------------------- [1.23510 s] survived - [# 5] CRP isPrime: -------------------------------------------------------------------------------- 2: def isPrime(num): 3: 4: if num > 1: 5: - 6: for i in range(2, num): + 6: for i in range(3, num): 7: if num % i == 0: 8: return False 9: else: 10: -------------------------------------------------------------------------------- [1.20360 s] survived - [# 6] CRP isPrime: -------------------------------------------------------------------------------- 3: 4: if num > 1: 5: 6: for i in range(2, num): - 7: if num % i == 0: + 7: if num % i == 1: 8: return False 9: else: 10: 11: return True -------------------------------------------------------------------------------- [1.23499 s] killed by testing program mutpy/test_isPrime.py::CalculatorTest::test_prime - [# 7] ROR isPrime: -------------------------------------------------------------------------------- 1: 2: def isPrime(num): 3: - 4: if num > 1: + 4: if num < 1: 5: 6: for i in range(2, num): 7: if num % i == 0: 8: return False -------------------------------------------------------------------------------- [1.24164 s] killed by testing program mutpy/test_isPrime.py::CalculatorTest::test_invalid - [# 8] ROR isPrime: -------------------------------------------------------------------------------- 1: 2: def isPrime(num): 3: - 4: if num > 1: + 4: if num >= 1: 5: 6: for i in range(2, num): 7: if num % i == 0: 8: return False -------------------------------------------------------------------------------- [1.21934 s] survived - [# 9] ROR isPrime: -------------------------------------------------------------------------------- 3: 4: if num > 1: 5: 6: for i in range(2, num): - 7: if num % i == 0: + 7: if num % i != 0: 8: return False 9: else: 10: 11: return True -------------------------------------------------------------------------------- [1.32597 s] killed by testing program mutpy/test_isPrime.py::CalculatorTest::test_prime [*] Mutation score [14.91747 s]: 66.7% - all: 9 - killed: 6 (66.7%) - survived: 3 (33.3%) - incompetent: 0 (0.0%) - timeout: 0 (
Podemos ver en la salida anterior, 6 mutantes han sido asesinados y solo 3 mutantes pudieron sobrevivir. Además, se logró una puntuación de mutación del 66,7%. Podemos mejorar aún más este puntaje de mutación analizando los mutantes que sobrevivieron a los casos de prueba y escribiendo casos de prueba nuevos o modificando para matar a los mutantes que sobrevivieron.