La prueba de software es el proceso en el que un desarrollador se asegura de que la salida real del software coincida con la salida deseada al proporcionar algunas entradas de prueba al software. La prueba de software es un paso importante porque, si se realiza correctamente, puede ayudar al desarrollador a encontrar errores en el software en muy poco tiempo.
Las pruebas de software se pueden dividir en dos clases, pruebas manuales y pruebas automatizadas . La prueba automatizada es la ejecución de sus pruebas utilizando un script en lugar de un humano. En este artículo, discutiremos algunos de los métodos de pruebas de software automatizadas con Python.
Escribamos una aplicación sencilla sobre la que realizaremos todas las pruebas.
class Square: def __init__(self, side): """ creates a square having the given side """ self.side = side def area(self): """ returns area of the square """ return self.side**2 def perimeter(self): """ returns perimeter of the square """ return 4 * self.side def __repr__(self): """ declares how a Square object should be printed """ s = 'Square with side = ' + str(self.side) + '\n' + \ 'Area = ' + str(self.area()) + '\n' + \ 'Perimeter = ' + str(self.perimeter()) return s if __name__ == '__main__': # read input from the user side = int(input('enter the side length to create a Square: ')) # create a square with the provided side square = Square(side) # print the created square print(square)
Nota: Para obtener más información sobre la función __repr__()
, consulte este artículo .
Ahora que tenemos nuestro software listo, echemos un vistazo a la estructura de directorios de nuestra carpeta de proyecto y después de eso, comenzaremos a probar nuestro software.
---Software_Testing |--- __init__.py (to initialize the directory as python package) |--- app.py (our software) |--- tests (folder to keep all test files) |--- __init__.py
El módulo ‘unittest’
Uno de los principales problemas con las pruebas manuales es que requiere tiempo y esfuerzo. En las pruebas manuales, probamos la aplicación sobre alguna entrada, si falla, lo anotamos o depuramos la aplicación para esa entrada de prueba en particular, y luego repetimos el proceso. Con unittest
, todas las entradas de prueba se pueden proporcionar a la vez y luego puede probar su aplicación. Al final, obtiene un informe detallado con todos los casos de prueba fallidos claramente especificados, si los hay.
El unittest
módulo tiene un marco de prueba incorporado y un corredor de prueba. Un marco de prueba es un conjunto de reglas que se deben seguir al escribir casos de prueba, mientras que un corredor de prueba es una herramienta que ejecuta estas pruebas con un montón de configuraciones y recopila los resultados.
Instalación: unittest
está disponible en PyPI y se puede instalar con el siguiente comando:
pip install unittest
Uso: Escribimos las pruebas en un módulo de Python (.py). Para ejecutar nuestras pruebas, simplemente ejecutamos el módulo de prueba utilizando cualquier IDE o terminal.
Ahora, escribamos algunas pruebas para nuestro pequeño software discutido anteriormente usando el unittest
módulo.
- Cree un archivo llamado
tests.py
en la carpeta llamada «pruebas». - En
tests.py
importaciónunittest
. - Cree una clase llamada
TestClass
que herede de la claseunittest.TestCase
.Regla 1: todas las pruebas se escriben como los métodos de una clase, que deben heredar de la clase
unittest.TestCase
. - Cree un método de prueba como se muestra a continuación.
Regla 2: el nombre de todos y cada uno de los métodos de prueba debe comenzar con «prueba», de lo contrario, el corredor de la prueba lo omitirá.def
test_area(
self
):
# testing the method Square.area().
sq
=
Square(
2
)
# creates a Square of side 2 units.
# test if the area of the above square is 4 units,
# display an error message if it's not.
self
.assertEqual(sq.area(),
4
,
f
'Area is shown {sq.area()} for side = {sq.side} units'
)
Regla 3: usamos
assertEqual()
declaraciones especiales en lugar de las declaraciones integradasassert
disponibles en Python.El primer argumento de
assertEqual()
es la salida real, el segundo argumento es la salida deseada y el tercer argumento es el mensaje de error que se mostraría en caso de que los dos valores difieran entre sí (la prueba falla). - Para ejecutar las pruebas que acabamos de definir, debemos llamar al método
unittest.main()
, agregar las siguientes líneas en el módulo «tests.py».if
__name__
=
=
'__main__'
:
unittest.main()
Debido a estas líneas, tan pronto como ejecute el script «test.py»,
unittest.main()
se llamará a la función y se ejecutarán todas las pruebas.
Finalmente, el módulo «tests.py» debería parecerse al código que se muestra a continuación.
import unittest from .. import app class TestSum(unittest.TestCase): def test_area(self): sq = app.Square(2) self.assertEqual(sq.area(), 4, f'Area is shown {sq.area()} rather than 9') if __name__ == '__main__': unittest.main()
Habiendo escrito nuestros casos de prueba, ahora probemos nuestra aplicación en busca de errores. Para probar su aplicación, simplemente necesita ejecutar el archivo de prueba «tests.py» usando el símbolo del sistema o cualquier IDE de su elección. La salida debería ser algo como esto.
. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
En la primera línea, un .
(punto) representa una prueba exitosa mientras que una ‘F’ representaría un caso de prueba fallido. El OK
mensaje, al final, nos dice que todas las pruebas fueron pasadas con éxito.
Agreguemos algunas pruebas más en «tests.py» y volvamos a probar nuestra aplicación.
import unittest from .. import app class TestSum(unittest.TestCase): def test_area(self): sq = app.Square(2) self.assertEqual(sq.area(), 4, f'Area is shown {sq.area()} rather than 9') def test_area_negative(self): sq = app.Square(-3) self.assertEqual(sq.area(), -1, f'Area is shown {sq.area()} rather than -1') def test_perimeter(self): sq = app.Square(5) self.assertEqual(sq.perimeter(), 20, f'Perimeter is {sq.perimeter()} rather than 20') def test_perimeter_negative(self): sq = app.Square(-6) self.assertEqual(sq.perimeter(), -1, f'Perimeter is {sq.perimeter()} rather than -1') if __name__ == '__main__': unittest.main()
.F.F ====================================================================== FAIL: test_area_negative (__main__.TestSum) ---------------------------------------------------------------------- Traceback (most recent call last): File "tests_unittest.py", line 11, in test_area_negative self.assertEqual(sq.area(), -1, f'Area is shown {sq.area()} rather than -1 for negative side length') AssertionError: 9 != -1 : Area is shown 9 rather than -1 for negative side length ====================================================================== FAIL: test_perimeter_negative (__main__.TestSum) ---------------------------------------------------------------------- Traceback (most recent call last): File "tests_unittest.py", line 19, in test_perimeter_negative self.assertEqual(sq.perimeter(), -1, f'Perimeter is {sq.perimeter()} rather than -1 for negative side length') AssertionError: -24 != -1 : Perimeter is -24 rather than -1 for negative side length ---------------------------------------------------------------------- Ran 4 tests in 0.001s FAILED (failures=2)
Algunas cosas a tener en cuenta en el informe de prueba anterior son:
- La primera línea representa que la prueba 1 y la prueba 3 se ejecutaron con éxito mientras que la prueba 2 y la prueba 4 fallaron
- Cada caso de prueba fallido se describe en el informe, la primera línea de la descripción contiene el nombre del caso de prueba fallido y la última línea contiene el mensaje de error que definimos para ese caso de prueba.
- Al final del informe puede ver el número de pruebas fallidas, si ninguna prueba falla, el informe terminará con
OK
Nota: Para mayor conocimiento puede leer la documentación completa de unittest
.
El módulo “nariz2”
El propósito de nose2
es ampliar unittest
para facilitar las pruebas. nose2
es compatible con las pruebas escritas con el unittest
marco de pruebas y se puede usar como reemplazo del ejecutor de unittest
pruebas.
Instalación: nose2
se puede instalar desde PyPI usando el comando,
pip install nose2
Uso: nose2
no tiene ningún marco de prueba y es simplemente un corredor de prueba que es compatible con el unittest
marco de prueba. Por lo tanto, ejecutaremos las mismas pruebas que escribimos anteriormente (para unittest
) usando nose2
. Para ejecutar las pruebas usamos el siguiente comando en el directorio fuente del proyecto («Software_Testing» en nuestro caso),
nose2
En nose2
terminología, todos los módulos de python (.py) cuyo nombre comienza con «test» (es decir, test_file.py, test_1.py) se consideran archivos de prueba. En la ejecución, nose2
buscará todos los archivos de prueba en todos los subdirectorios que se encuentran en una o más de las siguientes categorías,
- que son paquetes de python (contienen “__init__.py”).
- cuyo nombre comienza con «prueba» después de estar en minúsculas, es decir, TestFiles, pruebas.
- que se denominan «src» o «lib».
nose2
primero carga todos los archivos de prueba presentes en el proyecto y luego se ejecutan las pruebas. Por lo tanto, nose2
tenemos la libertad de dividir nuestras pruebas entre varios archivos de prueba en diferentes carpetas y ejecutarlas a la vez, lo cual es muy útil cuando se trata de una gran cantidad de pruebas.
Ahora aprendamos sobre las diferentes opciones de personalización proporcionadas por nose2
las cuales nos pueden ayudar durante el proceso de prueba.
- Cambiar el directorio de búsqueda:
si queremos cambiar el directorio en el quenose2
busca los archivos de prueba, podemos hacerlo usando los argumentos de la línea de comando-s
o--start-dir
como,nose2 -s DIR_ADD DIR_NAME
aquí,
DIR_NAME
es el directorio en el que queremos buscar los archivos de prueba yDIR_ADD
es la dirección del directorio principal enDIR_NAME
relación con el directorio de origen del proyecto (es decir, use «./» si el directorio de prueba está en el directorio de origen del proyecto).
Esto es extremadamente útil cuando desea probar solo una característica de su aplicación a la vez. - Ejecución de casos de prueba específicos: también podemos ejecutar una prueba específica a la vez utilizando los argumentos de la línea de comando
y como ,nose2
-s
--start-dir
nose2 -s DIR_ADD DIR_NAME.TEST_FILE.TEST_CLASS.TEST_NAME
- TEST_NAME: nombre del método de prueba.
- TEST_CLASS: clase en la que se define el método de prueba.
- TEST_FILE: nombre del archivo de prueba en el que se define el caso de prueba, es decir, test.py.
- DIR_NAME: directorio en el que existe el archivo de prueba.
- DIR_ADD: dirección del directorio padre de DIR_NAME relativo a la fuente del proyecto.
Con esta función, podemos probar nuestro software en entradas específicas.
- Ejecutar pruebas en un solo módulo:
nose2
también se puede usarunittest
llamando a la funciónnose2.main()
tal como la llamamosunittest.main()
en ejemplos anteriores.
Además de las personalizaciones básicas anteriores , nose2
proporciona funciones avanzadas como cargar varios complementos y archivos de configuración o crear su propio corredor de prueba.
El módulo «pytest»
pytest
es el marco de prueba más popular para python. Puede probar pytest
cualquier cosa, desde scripts básicos de python hasta bases de datos, API y UI. Aunque pytest
se usa principalmente para pruebas de API, en este artículo cubriremos solo los conceptos básicos de pytest
.
Instalación: puede instalar pytest
desde PyPI usando el comando,
pip install pytest
Uso:pytest
se llama al ejecutor de pruebas mediante el siguiente comando en el código fuente del proyecto ,
py.test
A diferencia nose2
de , pytest
busca archivos de prueba en todas las ubicaciones dentro del directorio del proyecto. Cualquier archivo cuyo nombre comience con «test_» o termine con «_test» se considera un archivo de prueba en la pytest
terminología. Vamos a crear un archivo «test_file1.py» en la carpeta «tests» como nuestro archivo de prueba.
Creación de métodos de prueba:pytest
admite los métodos de prueba escritos en el unittest
marco, pero el pytest
marco proporciona una sintaxis más sencilla para escribir pruebas. Consulte el código a continuación para comprender la sintaxis del método de prueba del pytest
marco.
from .. import app def test_file1_area(): sq = app.Square(2) assert sq.area() == 4, f"area for side {sq.side} units is {sq.area()}" def test_file1_perimeter(): sq = app.Square(-1) assert sq.perimeter() == -1, f'perimeter is shown {sq.perimeter()} rather than -1'
Nota: similar a unittest
, pytest
requiere que todos los nombres de prueba comiencen con «prueba».
A diferencia unittest
de , pytest
usa las assert
declaraciones de Python predeterminadas que lo hacen aún más fácil de usar.
Tenga en cuenta que ahora la carpeta «pruebas» contiene dos archivos, a saber, «tests.py» (escrito en el unittest
marco) y «test_file1.py» (escrito en el pytest
marco). Ahora vamos a ejecutar el pytest
corredor de prueba.
py.test
Obtendrá un informe similar al obtenido mediante el uso de unittest
.
============================= test session starts ============================== platform linux -- Python 3.6.7, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 rootdir: /home/manthan/articles/Software_testing_in_Python collected 6 items tests/test_file1.py .F [ 33%] tests/test_file2.py .F.F [100%] =================================== FAILURES ===================================
Los porcentajes en el lado derecho del informe muestran el porcentaje de pruebas que se han completado en ese momento, es decir, 2 de los 6 casos de prueba se completaron al final de «test_file1.py».
Aquí hay algunas personalizaciones básicas más que vienen con pytest
.
- Ejecución de archivos de prueba específicos: para ejecutar solo un archivo de prueba específico, use el comando,
py.test <filename>
- Coincidencia de substrings: supongamos que queremos probar solo el
area()
método de nuestraSquare
clase, podemos hacerlo usando la coincidencia de substrings de la siguiente manera:py.test -k "area"
Con este comando
pytest
se ejecutarán solo aquellas pruebas que tengan la string «área» en sus nombres, es decir, «test_file1_area()», «test_area()», etc. - Marcado: como sustituto de la coincidencia de substrings, el marcado es otro método con el que podemos ejecutar un conjunto específico de pruebas. En este método ponemos una marca en las pruebas que queremos ejecutar. Observe el ejemplo de código dado a continuación,
# @pytest.mark.<tag_name>
@pytest
.mark.area
def
test_file1_area():
sq
=
app.Square(
2
)
assert
sq.area()
=
=
4
,
f
"area for side {sq.side} units is {sq.area()}"
En el ejemplo de código anterior
test_file1_area()
está marcado con la etiqueta «área». Todos los métodos de prueba que han sido marcados con alguna etiqueta se pueden ejecutar usando el comando,py.test -m <tag_name>
- Procesamiento paralelo: si tiene una gran cantidad de pruebas,
pytest
puede personalizarlas para ejecutar estos métodos de prueba en paralelo. Para eso, debe instalarpytest-xdist
, que se puede instalar con el comando,pip install pytest-xdist
Ahora puede usar el siguiente comando para ejecutar sus pruebas más rápido usando multiprocesamiento,
py.test -n 4
Con este comando
pytest
asigna 4 trabajadores para realizar las pruebas en paralelo, puede cambiar este número según sus necesidades.Si sus pruebas son seguras para subprocesos, también puede usar subprocesos múltiples para acelerar el proceso de prueba. Para eso necesitas instalar
pytest-parallel
(usando pip). Para ejecutar sus pruebas en subprocesos múltiples, use el comando,pytest --workers 4
Publicación traducida automáticamente
Artículo escrito por Manthanchauhan y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA