Blog
Selenium Testing con Test Suite y Unittest
Realizar un script de automatización utilizando UnitTest y Selenium
Aplicar varios métodos de pruebas con UnitTest
En este tutorial de Selenium Python, cubriremos algunos de los temas clave sobre como automatizar las aplicaciones Web usando Selenium Webdriver APIs en Python.
Antes de comenzar con la automatización de pruebas, primero debemos preparar un conjunto de casos de prueba para las características que están activas en la aplicación Web. Estos pueden ser casos para criterios de aceptación o parte de pruebas funcionales.
A continuación, para la automatización, necesitaremos un marco de automatización o Test Framework que podría facilitar las capacidades de gestión de pruebas, como la creación de pruebas basadas en datos, la creación de condiciones previas y posteriores a las pruebas, la comprobación de los Test esperados y reales. Y lo que es más importante, debería proporcionar capacidad de generación de informes o reportes.
Como no todas estas funciones están disponibles en Selenium WebDriver utilizaremos el unit testing framework de Python y utilizaremos sus funciones junto con Selenium Webdriver.
Junto con este post, le recomendamos que lea también los siguientes tutoriales. Te ayudará a configurar Selenium con Python y configurar navegadores como Firefox, Chrome e IE.
- Selenium con Python ejemplo 2 con Selenium
- Navegando con Selenium
- Localizando elementos con Selenium
- Esperas de carga de los lugares webs
- Esquemas de Objetos en los lugares webs
- Ejemplo de Selenium con Facebook
Construya una suite de pruebas de python con Selenium desde cero con Unittest.
1. Entendiendo el Marco de Prueba de Python Unittest y sus características.
La librería Python Unittest hereda su raíz de un módulo de terceros conocido como PyUnit.
El módulo Python Unittest divide su funcionalidad en cinco componentes claves. Todos los cinco elementos trabajan en tándem para soportar las pruebas de automatización. Discutamos cada uno de ellos en detalle.
1.1. Cinco Componentes del Marco de Prueba de Python Unittest.
- Test Loader (Cargador de Pruebas) – Es una clase de Python que carga casos de prueba y suites creados localmente o desde una fuente de datos externa como un archivo. Libera un objeto TestSuite que lleva los casos y suites.
- TestCase (Caso de Prueba) – La clase TestCase contiene los manipuladores de prueba y proporciona ganchos para preparar cada manipulador y para limpiar después de la ejecución.
- Test Suite – Actúa como contenedor para agrupar los casos de prueba. Con la ayuda de una suite de pruebas, puede combinar un conjunto de casos de prueba que representan funcionalidades específicas de la aplicación bajo prueba.
- Test Runner – Proporciona una interfaz ejecutable para la ejecución de pruebas y entrega los resultados al usuario. Puede utilizar una GUI, un medio textual o devolver un código estándar para notificar los resultados de la ejecución de la prueba.
- TestReport (Reporte de Prueba) – Este componente organiza los resultados de la prueba, muestra el estado de aprobado/no aprobado de los casos de prueba ejecutados. Incluso proporciona los detalles de los pasos, el resumen de la ejecución global y el tiempo transcurrido en ejecución.
1.2. Prepare una Clase de Caso de Prueba para Automatizar un Escenario.
Podemos crear una o más pruebas heredando la clase TestCase disponible en el módulo Unittest. Para añadir un caso, también necesitamos proporcionar un método de prueba correspondiente (un manejador) a la clase derivada. Para finalizar un caso de prueba, podemos usar assert o cualquiera de sus variaciones para reportar el estado de prueba.
A continuación se presentan algunas de las funciones assert más comunes utilizadas casi en todas las pruebas.
- Utilizar assertEqual () para verificar un resultado esperado.
- Utilizar assertTrue () para verificar una condición.
- Utilizar assertRaises () para verificar que se plantee una excepción esperada.
Además del manejador de pruebas, también podemos agregar rutinas como setUp () y tearDown () para administrar la creación y disposición de cualquier objeto o condición que sea obligatoria para una prueba.
Empecemos ahora a usar la biblioteca de pruebas de Unit y escribamos una prueba sencilla heredando la clase TestCase. Para ello, necesitará importar el módulo <unittest> y definir una clase que herede la clase TestCase.
Vea el código de abajo para tener una idea de la clase de prueba:
import unittest from selenium import webdriver class SearchText(unittest.TestCase):
1.3. Uso del método setUp () para administrar los requisitos previos de la prueba.
Un método <setUp ()> funciona como punto de entrada para los casos de prueba. Podemos utilizarlo para ejecutar un conjunto fijo de acciones antes de ejecutar una prueba o todas las pruebas definidas en la clase.
Estos son prerrequisitos que pueden incluir las siguientes tareas de preparación de la configuración de pruebas.
- Cree una instancia de un controlador de navegador.
- Navegue a una URL base.
- Datos de pruebas de carga para su ejecución.
- Abra los archivos de registro para registrar entradas, estados y errores.
Este método no toma argumentos y no devuelve nada. Si un script tiene el método setUp () definido, entonces el corredor lo llamará primero antes de ejecutar cualquiera de los manejadores de prueba.
En nuestro ejemplo, estamos usando el método setUp () para crear una instancia de Firefox, configurar las propiedades y navegar a la página principal de la aplicación antes de ejecutar la prueba real.
import unittest from selenium import webdriver class SearchText(unittest.TestCase): def setUp(self): # crea la sesion con firefox self.driver = webdriver.Firefox(executable_path=r'C:\Users...añade tu ruta del... \geckodriver.exe') self.driver.implicitly_wait(30) self.driver.maximize_window() # navega hasta esa url self.driver.get("http://www.google.com/")
2. Empiece a escribir guiones de pruebas de python selenium usando Unittest.
2.1. Crea tu primera prueba de selenium en Python con Unittest.
Después de crear un método setUp (), ahora podemos escribir algunas pruebas para verificar la funcionalidad de la aplicación. Así que, en primer lugar, definamos nuestro caso de uso.
En este ejemplo, buscaremos un texto en google y verificaremos si la búsqueda devuelve una lista de elementos.
Similar al <setUp ()> método, los métodos de prueba se implementan en la clase TestCase. Al agregar estos métodos, es una buena práctica prefijar sus nombres con la prueba de palabras. Tener tal nombre ayuda a Test Runner a distinguir entre una prueba y otros métodos. Echa un vistazo al siguiente guión para demostrar el ejemplo dado de Python de selenium.
import unittest from selenium import webdriver class SearchText(unittest.TestCase): def setUp(self): # crea la nueva seccion del nagegador self.driver = webdriver.Firefox(executable_path=r'C:\Users...añade tu ruta del... \geckodriver.exe') self.driver.implicitly_wait(30) self.driver.maximize_window() # navega hasta la url self.driver.get("http://www.google.com/") def test_search_by_text(self): # obtener el cuadro de texto de búsqueda self.search_field = self.driver.find_element_by_name("q") # escribe la palabra clave de búsqueda y la envía self.search_field.send_keys("aprender python") self.search_field.submit() #obtener la lista de elementos que se muestran después de la búsqueda #actualmente en la página de resultados usando find_elements_by_class_namemethod lists = self.driver.find_elements_by_class_name("r") no=len(lists) self.assertEqual(10, len(lists))
2.2. Definir estrategia de limpieza a recursos libres después de la ejecución de la prueba.
Una vez finalizada la ejecución de la prueba, hay que limpiar los prerrequisitos especificados en el método setUp ().
Para lograr esto, la clase base TestCase proporciona otro método, por ejemplo, tearDown () que el corredor llama después de la ejecución de la prueba. Nos permite limpiar los valores inicializados al principio de la prueba mediante el método setUp ().
En nuestro ejemplo, cuando finaliza la ejecución de prueba, ya no necesitamos la instancia de Firefox. Así que lo cerraremos en el método tearDown (), como se muestra en el siguiente código.
import unittest from selenium import webdriver class SearchText(unittest.TestCase): def setUp(self): # se crea la nueva seccion de firefox self.driver = webdriver.Firefox(executable_path=r'C:\Users...añade tu ruta del... \geckodriver.exe') self.driver.implicitly_wait(30) self.driver.maximize_window() # navaga hasta la url self.driver.get("http://www.google.com/") def test_search_by_text(self): # se obtiene el cuadro de busquedas self.search_field = self.driver.find_element_by_name("q") # se escribe la plaabra clave y se submite self.search_field.send_keys("aprender python") self.search_field.submit() #obtener la lista de elementos que se muestran después de la búsqueda #actualmente en la página de resultados usando find_elements_by_class_namemethod lists = self.driver.find_elements_by_class_name("r") no=len(lists) self.assertEqual(10, len(lists)) def tearDown(self): # close the browser window self.driver.quit()
¿Cómo ejecutar el caso de prueba desde la línea de comandos?
Ejecutar las pruebas desde la línea de comandos requeriría que agreguemos una llamada al método main () en el script de prueba. También pasaremos un argumento al main (). Aparecerán los detalles del resultado de los Test en la consola.
A continuación se muestra el código para facilitar la ejecución de la línea de comandos. Tendremos que añadir éstas dos líneas de código justo al final.
if __name__ == '__main__': unittest.main()
Cuando ejecutas el programa debería aparecer:
---------------------------------------------------------------------- Ran 1 test in 7.081s OK
Si modificas la línea 26 por “self.assertEqual(11, len(lists))” tiene que salir un error como este cuando ejecutes el código, ya que va a comprar 11 con 10 (los resultados de 1º pagina de google siempre son 10):
len(lists) F ====================================================================== FAIL: test_search_by_text (__main__.SearchText) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:/Users/...selenium/11ejem.py", line 34, in test_search_by_text self.assertEqual(11, len(lists)) AssertionError: 11 != 10 ---------------------------------------------------------------------- Ran 1 test in 7.684s FAILED (failures=1)
Añada un TestCase más.
Hasta ahora, hemos automatizado un simple caso de prueba. Pero podemos añadir tantos casos como se necesite en la clase TestCase. También ayudará a crear grupos lógicos de pruebas relacionadas con una funcionalidad específica. Así que agreguemos otra prueba a la clase TestCase. El nombre el nuevo método empieza con la palabra test, como se muestra en el siguiente código.
def test_search_by_name(self): # obtener el cuadro de texto de búsqueda self.search_field = self.driver.find_element_by_name("q") # se introduce la palabra clave y se submite self.search_field.send_keys("Python class") self.search_field.submit() #obtener la lista de elementos que se muestran después de la búsqueda #actualmente en la página de resultados usando find_elements_by_class_namemethod list_new = self.driver.find_elements_by_class_name("r") self.assertEqual(10, len(list_new))
Ejecutar el TestClass resultaría en abrir primero y luego cerrar las dos instancias de Firefox. Así es como funcionan los métodos setUp () y tearDown () para cada método de prueba. A continuación los resultados de los 2 Test:
====================================================================== FAIL: test_search_by_name (__main__.SearchText) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:/Users/.../selenium/11ejem.py", line 45, in test_search_by_name self.assertEqual(10, len(list_new)) AssertionError: 10 != 11 ---------------------------------------------------------------------- Ran 2 tests in 14.740s FAILED (failures=1)
Refactoring setUp () y tearDown () Métodos de optimización.
En los ejemplos anteriores, estábamos usando el método setUp () para crear instancias del controlador Firefox. Pero este enfoque estaba conduciendo a la creación de una nueva instancia del navegador web cada vez que se ejecutaba un nuevo caso de prueba.
Fue el método setUp () el que estaba causando este comportamiento mientras se ejecuta antes de cada caso de prueba. El mismo caso es con el método tearDown () que dispara para cada caso de prueba una vez finalizada la ejecución.
Así que podemos refactorizar nuestro script para minimizar el uso de recursos. Esto significa que podemos compartir una única instancia de Firefox entre los métodos en lugar de crear una nueva instancia cada vez.
Es posible utilizando los métodos setUpClass () y tearDownClass () junto con el decorador @classmethod. Estos métodos nos permiten establecer los valores a nivel de clase en lugar de a nivel de método. Los valores inicializados a nivel de clase se comparten entre los métodos de prueba.
Veamos el ejemplo anterior con código modificado para llamar a los métodos setUpClass () y tearDownClass () con el decorador @classmethod. Ejemplo:
import unittest from selenium import webdriver class SearchText(unittest.TestCase): @classmethod def setUp(self): # se crea la nueva seccion de firefox self.driver = webdriver.Firefox(executable_path=r'C:\Users...añade tu ruta del... \geckodriver.exe') self.driver.implicitly_wait(30) self.driver.maximize_window() # va a la url self.driver.get("http://www.google.com/") def test_search_by_text(self): # se obtiene la caja de busquedas self.search_field = self.driver.find_element_by_name("q") # se introduce la palabra clave y se submite self.search_field.send_keys("aprender python") self.search_field.submit() #obtener la lista de elementos que se muestran después de la búsqueda #actualmente en la página de resultados usando find_elements_by_class_namemethod lists = self.driver.find_elements_by_class_name("r") no=len(lists) self.assertEqual(10, len(lists)) def test_search_by_name(self): # se obtiene la caja de busquedas self.search_field = self.driver.find_element_by_name("q") # se escribe la palabra clave y se submite self.search_field.send_keys("Python") self.search_field.submit() #obtener la lista de elementos que se muestran después de la búsqueda #actualmente en la página de resultados usando find_elements_by_class_namemethod list_new = self.driver.find_elements_by_class_name("r") self.assertEqual(10, len(list_new)) @classmethod def tearDown(self): # se cierra la venta del navegador self.driver.quit() if __name__ == '__main__': unittest.main()
Al ejecutar la prueba, podemos ver que ambas pruebas se están ejecutando en el mismo navegador Firefox.
Assertions/Afirmaciones en el Unittest Framework de Python
La clase TestCase de la biblioteca Python Unittest implementa una lista de métodos assert. Podemos utilizarlos para hacer coincidir los valores reales devueltos por la aplicación con los valores esperados. Con cada método, podemos especificar una condición que debe ser verdadera para seguir ejecutando la prueba.
Los siguientes tres tipos de assert/afirmación están disponibles.
1. Comprobar equivalencia.
2. Comparación lógica.
3. Actuar en caso de Excepciones.
Mientras se ejecuta un test, la ejecución se mueve a la siguiente línea sólo si la aserción dada pasa. De lo contrario, la prueba se detendría inmediatamente y aparecería un mensaje de fallo.
Veamos una lista importante de métodos de afirmación.
Lista de métodos de afirmación en el módulo Python Unittest:
Assert Método | Test Condición | Sumario |
assertEqual(a, b [,msg]) | a==b | Compruebe si “a” y “b” coinciden o no. También puede pasar un mensaje de error. |
assertNotEqual(a,b[,msg]) | a!=b | ejemplo assertEqual(element.text,”10″) |
assertTrue(x[,msg])) | bool(x) is True | Verificar si la expresión dada evalúa Verdadero o Falso. |
assertFalse(x[,msg])) | bool(x) isFalse | ejemplo assertTrue(element.is_displayed()) |
assertIsNot(a, b[,msg])) | a no es b | |
assertRaises(exc, fun,*args, **kwds) |
fun(*args,**kwds) raises exc |
Compruebe si el paso de prueba sube la Excepción específica mencionada. Un ejemplo de ello es utilizar este método para comprobar <NoSuchElementFoundexception>. |
assertRaisesRegexp(exc, r, fun, *args, **kwds) |
fun(*args,**kwds)raises excand themessagematchesregex r | |
assertAlmostEqual(a, b) | round(a-b,7) == 0 | Compara los valores numéricos después de redondearlos con el número del segundo argumento. |
assertNotAlmostEqual(a,b) | round(a-b,7) != 0 | |
assertGreater(a, b) | a > b | |
assertGreaterEqual(a,b) | a>=b | |
assertRegexpMatches(s, r) | r.search(s) | Verifique si una búsqueda repetida coincide con el texto. |
assertNotRegexpMatches(s, r) | not r.search(s) | |
assertMultiLineEqual(a, b) | strings | Este método es una extensión del assertEqual (), diseñado para cadenas multilíneas. |
assertListEqual(a, b) | listas | Este método verifica si las listas “a” y “b” coinciden. Ayuda a trabajar con los campos desplegables. |
fail() | Este método falla la prueba incondicionalmente. Permite la creación de bloques condicionales personalizados. |
Cree una Test suite con Selenium utilizando Unittest.
El módulo Unittest tiene una clase TestSuite que facilita la creación de un conjunto de pruebas de punto a punto de Selenium Python Test Suite. Con esta característica, podemos combinar varias pruebas en grupos lógicos y convertirlas en una suite de pruebas unificada. Todo esto es posible usando las clases TestSuite, TestLoader y TestRunner.
Antes de entrar en detalles de TestSuite, agreguemos una nueva prueba para revisar la página de inicio de la aplicación en prueba. Agregaremos esta prueba junto con las pruebas de búsqueda anteriores en un solo conjunto de pruebas, como se muestra en el siguiente código:
import unittest from selenium import webdriver from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.common.by import By class HomePageTest(unittest.TestCase): @classmethod def setUp(inst): # crea una nueva Firefox sesion """ inst.driver = webdriver.Firefox() inst.driver.implicitly_wait(30) inst.driver.maximize_window() # navega a la pagina """ inst.driver.get("http://www.google.com/") def test_search_box(self): # marque la casilla de búsqueda en la página de inicio self.assertTrue(self.is_element_present(By.NAME,"q")) def test_language_settings(self): # verifique las opciones de idioma en la página de inicio self.assertTrue(self.is_element_present(By.ID,"_eEe")) def test_images_link(self): # ver enlace de imágenes en la página de inicio images_link = self.driver.find_element_by_link_text("Images") images_link.click() # comprobar el campo de búsqueda existe en la página de imágenes self.assertTrue(self.is_element_present(By.NAME,"q")) self.search_field = self.driver.find_element_by_name("q") # ecribe la palabra y submitela self.search_field.send_keys("Selenium Webdriver framework architecture diagram") self.search_field.submit() @classmethod def tearDown(inst): # cierra el navegador inst.driver.quit() def is_element_present(self, how, what): """ Metodo auxiliar para confirmar la presencia de un elemento en la página : param how: por tipo de localizador : params what: valor del localizador """ try: self.driver.find_element(by=how, value=what) except NoSuchElementException: return False return True if __name__ == '__main__': unittest.main(verbosity=2)
Agrupe los casos de prueba en una suite de pruebas:
Ahora habrías entendido muy bien que usaremos la clase TestSuite para definir y ejecutar el conjunto de pruebas. Y podemos agregar múltiples casos de prueba en él. Además, aparte de la clase TestSuite, necesitamos usar las clases TestLoader y TextTestRunner para crear y ejecutar una suite de pruebas. Consulte el siguiente código.
import unittest from SeleniumPythonRefactorTestCase import SearchText from SeleniumPythonMultipleTests import HomePageTest # se obtienen los test desde SearchText y la clase HomePageTest search_text = unittest.TestLoader().loadTestsFromTestCase(SearchText) home_page_test = unittest.TestLoader().loadTestsFromTestCase(HomePageTest) # se crea el pack de test combinando search_text y home_page_test test_suite = unittest.TestSuite([home_page_test, search_text]) # se ejecuta el pack de test unittest.TextTestRunner(verbosity=2).run(test_suite)
Ejecute la suite de test
La clase TestLoader lee todos los métodos de prueba de los archivos de prueba especificados que contienen la definición del conjunto de pruebas. A continuación, la clase TestRunner toma el control del conjunto de pruebas y ejecuta todas las pruebas especificadas. A continuación se muestra el comando para ejecutar el nuevo script de la suite de pruebas.
Para ejecutar todo necesitas los dos códigos antes mencionados:
- SeleniumPythonRefactorTestCase
- SeleniumPythonMultipleTests
Después tener estos dos códigos (son lo que estan mas arriba de este post) creo otro archivo y copia el código de Agrupe los casos de prueba en una suite de pruebas y ejecútalo. Esto ejecutará todas las pruebas de las clases SearchText y HomePage y generará una salida similar a esta:
======================================================================
FAIL: test_search_by_name (SeleniumPythonRefactorTestCase.SearchText)
———————————————————————-
Traceback (most recent call last):
File “C:\Users\……\selenium\SeleniumPythonRefactorTestCase.py”, line 46, in test_search_by_name
self.assertEqual(10, len(list_new))
AssertionError: 10 != 11
———————————————————————-
Ran 5 tests in 68.177s
FAILED (failures=1, errors=2)
Generar Informe de Ejecución de HTML Test Suite.
De forma predeterminada, la biblioteca Python Unittest emite la salida de prueba en la consola terminal. Si desea compartir los resultados con la administración y las partes interesadas, enviar los registros de la consola no es la forma adecuada.
Así que necesitas generar un informe que parezca presentable y profesional. Un informe resumido con un buen formato, con un acceso detallado a los detalles es lo que se requiere.
Puesto que la biblioteca de pruebas de unidad no tiene la capacidad de producir tal informe, por lo que debe utilizar la extensión HTMLTestRunner.
Para descargar el HTMLTestRunner, por favor siga el siguiente enlace.
Para integrar HTMLTestRunnersupport en nuestro script de prueba, necesitamos crear un archivo de salida para almacenar el informe actual, configurar las opciones de HTMLTestRunner, y ejecutar las pruebas de la siguiente manera.
Ejemplo de un Test suite en python con selenium:
import unittest import HTMLTestRunner import os from SeleniumPythonRefactorTestCase import SearchText from SeleniumPythonMultipleTests import HomePageTest # obtener la ruta del directorio al archivo de informe de salida dir = os.getcwd() # se obtienen los test desde SearchText y la clase HomePageTest search_text = unittest.TestLoader().loadTestsFromTestCase(SearchText) home_page_test = unittest.TestLoader().loadTestsFromTestCase(HomePageTest) # se crea el pack de test combinando con search_text and home_page_test test_suite = unittest.TestSuite([home_page_test, search_text]) # abre el archivo de reporte outfile = open(dir + "\SeleniumPythonTestSummary.html", "w") # configura las opciones de HTMLTestRunner runner = HTMLTestRunner.HTMLTestRunner(stream=outfile,title='Test Report', description='Acceptance Tests') # ejecuta la suite usando HTMLTestRunner runner.run(test_suite)
a
a
➡ Aprende mucho mas de como utilizar Selenium en nuestro Curso de Selenium con Python:
[…] Siguiente lección -> Selenium Testing con Test Suite y Unittest […]
Hola no he podido correr varias pruebas en un solo caso de pruebas, me ayudan por favor??.
Hola Andrés, ¿puedes ser un poco mas específico para saber que necesitas? gracias por comentar, un saludo!
en el ejemplo con decorador classmethod en vez de las funciones setUpClass y tearDownClass que recomienda el artículo aparecen usadas setUp y tearDown, de esa forma no me anduvo aunque sí luego de corregirlo a la forma descrita en el contenido.
Genial, me funciono a la perfeccion, lo unico que no pude hacer es el tearDown, asique tuve que poner un self.driver.close() para cerra el navegador dsp de la prueba