Introducción a Selenium 4.4 con Python, pruebas a diferentes paginas e implementación de logs

Selenium 4.4 con Python pruebas a diferentes paginas e implementación de logs

<Hola>, en el post anterior vimos cómo realizar una búsqueda de los selectores ID y CLASS, ahora vamos a visitar 2 páginas web más del proyecto y a controlar los errores que nos marca, además tendremos la opción de guardar algunos logs.

Lo que buscamos es tener un mensaje mas certero al tener un error, en la siguiente imagen vemos los mensajes sin control de errores

Mensajes del código sin control de errores

Aquí con control de errores

Mensaje con control de errores

Modificamos un poco el inicio del proyecto

from selenium import webdriver

from process.frontend.home import ProcessHomeFront
from process.frontend.contact import ProcessContactFront
from process.frontend.blog import ProcessBlogFront

#open browser
driver = webdriver.Chrome()
driver.get("http://localhost/")

process_home_front = ProcessHomeFront( driver )

process_home_front.start_process()

process_contact_front = ProcessContactFront( driver )

process_contact_front.start_process()

process_blog_front = ProcessBlogFront( driver )

process_blog_front.start_process()

driver.quit()

Veremos unas modificaciones, se agregan por ejemplo 3 clases nuevas, cada una con su segmento de código con el fin de asignar a cada una sus propias pruebas, nos vamos con la clase “ProcessHomeFront”, vemos el código a continuación

from own_class.errorselenium import ErrorSelenium

class ProcessHomeFront:

    def __init__(self, webdriver):
        self.webdriver = webdriver

    def start_process(self):
        detect_error = ErrorSelenium( self.webdriver )
        detect_error.exist_element_by_class("copyright_left")

Vamos a desglosar el código, al inicio importamos una pequeña librería que adaptamos para el manejo de errores

from own_class.errorselenium import ErrorSelenium

Declaramos la clase

class ProcessHomeFront:

y también declaramos el constructor donde esperamos el parámetro del “webdriver” de selenium

def __init__(self, webdriver):
        self.webdriver = webdrive

Para terminar con la declaración de una función

def start_process(self):
        detect_error = ErrorSelenium( self.webdriver )
        detect_error.exist_element_by_class("copyright_left")

Lo que hace esta función es verificar si existe la clase “copyright_left” en el contenido html, el siguiente fragmento es donde válida esto, más tarde veremos el código completo de la clase “ErrorSelenium”, por el momento dejo un fragmento del código

def exist_element_by_class(self, arg):
        try:
            self.webdriver.find_element(By.CLASS_NAME, arg)
        except NoSuchElementException:
            error = self.webdriver.find_element(By.CLASS_NAME, "line-clamp-2").text
            
            get_url = self.webdriver.current_url

            print("The class do not exist")
            print(f"{bcolors.FAIL} MSG: {error} | PAGE: {str(get_url)} {bcolors.ENDC}")
            logging.warning( f"MSG: {error} | PAGE: {str(get_url)}" )

            self.webdriver.quit()
            quit()

Lo que he visto en varios tutoriales es que colocan el siguiente código

self.webdriver.find_element(By.CLASS_NAME, arg)

sin control de excepciones, intente hacer un manejador de errores con las sentencias “try” y “except”, espero que nos sirva para este tutorial. Como podemos observar la primera línea de este fragmento de código es para declarar la función.

def exist_element_by_class(self, arg):

Esta función recibe 2 parámetros uno propio de python y el otro parámetro es el selector clase que nosotros queremos que busque. En el siguiente bloque de código utilizaremos la sentencia “try”

try:
            self.webdriver.find_element(By.CLASS_NAME, arg)

esta función se activa si dentro de la ejecución aparece un error, en caso de que exista el error se ejecuta el siguiente bloque

except NoSuchElementException:
            error = self.webdriver.find_element(By.CLASS_NAME, "line-clamp-2").text
            
            get_url = self.webdriver.current_url

            print("The class do not exist")
            print(f"{bcolors.FAIL} MSG: {error} | PAGE: {str(get_url)} {bcolors.ENDC}")
            logging.warning( f"MSG: {error} | PAGE: {str(get_url)}" )

            self.webdriver.quit()
            quit()

La palabra o sentencia “except” es una palabra predefinida del lenguaje Python, la cual tiene un parámetro de error “NoSuchElementException”, que nos ayudará a detectar el error de cuando no existe la “clase” o “id”, el proyecto que estoy haciendo pruebas esta hecho en Laravel por eso el motivo de la siguiente línea de código

 error = self.webdriver.find_element(By.CLASS_NAME, "line-clamp-2").text

en Laravel cuando encuentra un error se despliega una vista con el detalle del error, lo que hacemos aquí es buscar el mensaje principal del error, con la siguiente línea de código obtenemos la Url

get_url = self.webdriver.current_url

Ahora imprimimos los mensajes, el primer mensaje nos notifica que no existe la clase y luego de esto se imprime en colores rojo el mensaje de error de Laravel y la página donde está sucediendo.

print("The class do not exist")
print(f"{bcolors.FAIL} MSG: {error} | PAGE: {str(get_url)} {bcolors.ENDC}")

Para tener un registro de lo que está sucediendo, se activó la librería Log, el cual nos llevará un historial de los errores de la aplicación

logging.warning( f"MSG: {error} | PAGE: {str(get_url)}" )
Registro de las actividades del código

Por último las siguiente dos líneas de código nos ayudará a cerrar el proceso del “webdriver” y del proceso principal.

self.webdriver.quit()
quit()

Las otras dos clases son iguales a ProcessHomeFront, este archivo es  ProcessContactFront.py, el código es

from own_class.errorselenium import ErrorSelenium

class ProcessContactFront:

    def __init__(self, webdriver):
        self.webdriver = webdriver

    def start_process(self):
        self.webdriver.get('http://localhost/contact')
        detect_error = ErrorSelenium( self.webdriver )
        detect_error.exist_element_by_class("copyright_left")

Y por último la clase ProcessBlogFront

from own_class.errorselenium import ErrorSelenium

class ProcessBlogFront:

    def __init__(self, webdriver):
        self.webdriver = webdriver

    def start_process(self):
        self.webdriver.get('http://localhost/blog')
        detect_error = ErrorSelenium( self.webdriver )
        detect_error.exist_element_by_class("copyright_left")

Las líneas de código son prácticamente igual, solo cambia que se agrega la línea de código “self.webdriver.get”, que nos permite redirigir el navegador hacia esa página, en este segmento nos vamos a enfocar en la clase que importamos al inicio, “ErrorSelenium”, aquí está el código

import logging
import datetime

from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By

from .bcolors import bcolors

class ErrorSelenium:

    def __init__(self, webdriver):
        self.webdriver = webdriver

        x = datetime.datetime.now()
        logging.basicConfig(filename='my-project/tests/Selenium/myapp.log', level=logging.INFO)
        logging.info(f"Started {x}")

    def exist_element_by_class(self, arg):
        try:
            self.webdriver.find_element(By.CLASS_NAME, arg)
        except NoSuchElementException:
            error = self.webdriver.find_element(By.CLASS_NAME, "line-clamp-2").text
            
            get_url = self.webdriver.current_url

            print("The class do not exist")
            print(f"{bcolors.FAIL} MSG: {error} | PAGE: {str(get_url)} {bcolors.ENDC}")
            logging.warning( f"MSG: {error} | PAGE: {str(get_url)}" )

            self.webdriver.quit()
            quit()

    def exist_element_by_id(self, arg):
        try:
            self.webdriver.find_element(By.ID, arg)
        except NoSuchElementException:
            error = self.webdriver.find_element(By.CLASS_NAME, "line-clamp-2").text
            
            get_url = self.webdriver.current_url

            print("The id do not exist")
            print(f"{bcolors.FAIL} MSG: {error} | PAGE: {str(get_url)} {bcolors.ENDC}")
            logging.warning( f"MSG: {error} | PAGE: {str(get_url)}" )

            self.webdriver.quit()
            quit()

Vamos a desglosar un poco el código, en las dos primeras líneas de código podemos observar que se importa las librerías, una para guardar un registro de las actividades y la otra librería para utilizar fechas

import logging
import datetime

También importamos librerías dedicadas a Selenium,

from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By

Por último de las importaciones agregamos una pequeña clase que nos servirá para ver los mensajes de la terminal en colores.

from .bcolors import bcolors

En el siguiente código, vemos al constructor

def __init__(self, webdriver):
        self.webdriver = webdriver

        x = datetime.datetime.now()
        logging.basicConfig(filename='my-project/tests/Selenium/myapp.log', level=logging.INFO)
        logging.info(f"Started {x}")

El constructor necesita el “webdriver” como parámetro para realizar las comprobaciones, también podemos observar el uso de un log que nos permite saber en qué fecha fue ejecutado. A continuación veremos la primera función que nos servirá para buscar clases en el contenido HTML

def exist_element_by_class(self, arg):
        try:
            self.webdriver.find_element(By.CLASS_NAME, arg)
        except NoSuchElementException:
            error = self.webdriver.find_element(By.CLASS_NAME, "line-clamp-2").text
            
            get_url = self.webdriver.current_url

            print("The class do not exist")
            print(f"{bcolors.FAIL} MSG: {error} | PAGE: {str(get_url)} {bcolors.ENDC}")
            logging.warning( f"MSG: {error} | PAGE: {str(get_url)}" )

            self.webdriver.quit()
            quit()

Esta función espera un argumento que es el nombre de la clase, si no colocamos el “try” la función “find_element” nos devuelve un error muy detallada que por el momento no deseamos, en caso de encontrar ese error buscamos la clase “line-clamp-2” que nos proporciona laravel cuando un error pasa, este mensaje será incluido en los logs y será mostrado en la ejecución del código.

Seguimos con la función “exist_element_by_id”

def exist_element_by_id(self, arg):
        try:
            self.webdriver.find_element(By.ID, arg)
        except NoSuchElementException:
            error = self.webdriver.find_element(By.CLASS_NAME, "line-clamp-2").text
            
            get_url = self.webdriver.current_url

            print("The id do not exist")
            print(f"{bcolors.FAIL} MSG: {error} | PAGE: {str(get_url)} {bcolors.ENDC}")
            logging.warning( f"MSG: {error} | PAGE: {str(get_url)}" )

            self.webdriver.quit()
            quit()

Es prácticamente igual que la función “exist_element_by_class”, en caso de no encontrar el “id” que se pasa a través del argumento, este busca la clase “line-clamp-2” de laravel para mostrar el mensaje de error.

Me faltaba incluir la clase que maneja los colores en el texto de la terminal.

class bcolors:

    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

Para ejecutar las pruebas solo ejecutamos el siguiente comando

python .\{ruta}\start.py
Iniciar proyecto de Selenium desde la consola

Con esto concluimos este pequeño tutorial, que nos permite saber cómo visitar 1 o más páginas dentro de la prueba, manejar errores cuando no se encuentran los selectores “class” e “id”, también agregamos el registro de las actividades que realiza el código.

</Saludos>

Deja un comentario