Diferencia entre revisiones de «Tutorial Campo de entrenamiento»
m |
|||
(No se muestran 9 ediciones intermedias de 3 usuarios) | |||
Línea 25: | Línea 25: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
− | $ pip install flask pytest pytest-cov selenium locust | + | $ pip install flask pytest pytest-cov selenium locust webdriver-manager |
</syntaxhighlight> | </syntaxhighlight> | ||
Línea 263: | Línea 263: | ||
Estas pruebas simulan la interacción de un usuario con la interfaz web de la aplicación. | Estas pruebas simulan la interacción de un usuario con la interfaz web de la aplicación. | ||
+ | |||
+ | Importante: hay que tener instalados los siguientes paquetes y en este orden: | ||
+ | |||
+ | <code> | ||
+ | sudo dpkg -i <archivo deb oficial descargado> [https://www.google.com/intl/es_es/chrome/?brand=FHFK&ds_kid=43700059038707842&gad_source=1&gclid=EAIaIQobChMIgKjcmdmSiQMV4jsGAB3_2AtqEAAYASAAEgJP2PD_BwE&gclsrc=aw.ds https://www.google.com/intl/es_es/chrome/?brand=FHFK&ds_kid=43700059038707842&gad_source=1&gclid=EAIaIQobChMIgKjcmdmSiQMV4jsGAB3_2AtqEAAYASAAEgJP2PD_BwE&gclsrc=aw.ds] | ||
+ | |||
+ | sudo apt install chromium-browser | ||
+ | |||
+ | sudo apt install chromium-driver | ||
+ | </code> | ||
===== Un pasito previo para ver <code>Selenium</code> en acción ===== | ===== Un pasito previo para ver <code>Selenium</code> en acción ===== | ||
Antes de realizar las pruebas sobre nuestra aplicación vamos a hacer un pasito previo para comprobar que tenemos chromium y chromium-driver correctamente instalados y entender el tipo de cosas que podemos hacer con <code>Selenium</code>. | Antes de realizar las pruebas sobre nuestra aplicación vamos a hacer un pasito previo para comprobar que tenemos chromium y chromium-driver correctamente instalados y entender el tipo de cosas que podemos hacer con <code>Selenium</code>. | ||
+ | |||
+ | Si no tienes Chrome instalado en tu equipo puedes simplemente instalar en tu sistema los paquetes <code>chromium-brwoser</code> y <code>chromium-driver</code>. Pero si ya tenías Chrome instalado tendrás que instalar el paquete <i>webdriver-manager</i> que se encargará de instalar las versiones adecuadas de Chromium y Chromium Webdriver. | ||
+ | |||
+ | <code>pip install webdriver-manager</code> | ||
====== Archivo <code>tests/test_selenium.py</code> : ====== | ====== Archivo <code>tests/test_selenium.py</code> : ====== | ||
Línea 276: | Línea 290: | ||
from selenium.webdriver.support.ui import WebDriverWait | from selenium.webdriver.support.ui import WebDriverWait | ||
from selenium.webdriver.support import expected_conditions as EC | from selenium.webdriver.support import expected_conditions as EC | ||
+ | from webdriver_manager.chrome import ChromeDriverManager | ||
+ | from selenium.webdriver.chrome.service import Service | ||
# Configurar Selenium para usar Chromium | # Configurar Selenium para usar Chromium | ||
options = webdriver.ChromeOptions() | options = webdriver.ChromeOptions() | ||
+ | |||
# Quita '--headless' para ejecutar el navegador de manera visible | # Quita '--headless' para ejecutar el navegador de manera visible | ||
options.add_argument('--no-sandbox') | options.add_argument('--no-sandbox') | ||
options.add_argument('--disable-dev-shm-usage') | options.add_argument('--disable-dev-shm-usage') | ||
− | # Iniciar el driver de Chromium | + | # Iniciar el driver de Chromium usando webdriver-manager |
− | driver = webdriver.Chrome(options=options) | + | service = Service(ChromeDriverManager().install()) |
+ | # Si obtienes un error al lanzar la prueba, puedes probar a cambiar la línea anterior por la siguiente | ||
+ | # service = Service('/usr/bin/chromedriver') # Pero tendrás que cerciorarte de la ruta exacta al driver en tu equipo | ||
+ | |||
+ | driver = webdriver.Chrome(service=service, options=options) | ||
try: | try: | ||
Línea 317: | Línea 338: | ||
# Cerrar el navegador | # Cerrar el navegador | ||
driver.quit() | driver.quit() | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Si prefieres usar Firefox como navegador para tus pruebas con Selenium, puedes usar el siguiente script proporcionado por ''Pablo Caballero del G3'': | ||
+ | |||
+ | <syntaxhighlight lang="python"> | ||
+ | |||
+ | from selenium import webdriver | ||
+ | from selenium.webdriver.common.by import By | ||
+ | from selenium.webdriver.common.keys import Keys | ||
+ | from selenium.webdriver.support.ui import WebDriverWait | ||
+ | from selenium.webdriver.support import expected_conditions as EC | ||
+ | from webdriver_manager.firefox import GeckoDriverManager | ||
+ | from selenium.webdriver.firefox.service import Service | ||
+ | |||
+ | |||
+ | # Configurar Selenium para usar Firefox | ||
+ | options = webdriver.FirefoxOptions() | ||
+ | options.add_argument('--no-sandbox') | ||
+ | options.add_argument('--disable-dev-shm-usage') | ||
+ | |||
+ | # Quitar "--headless" si quieres ver el navegador en modo visible | ||
+ | # options.add_argument("--headless") # Puedes quitar esto para modo visible | ||
+ | |||
+ | # Iniciar el driver de Firefox usando webdriver-manager | ||
+ | service = Service(GeckoDriverManager().install()) | ||
+ | driver = webdriver.Firefox(service=service, options=options) | ||
+ | |||
+ | try: | ||
+ | # 1. Abrir Google | ||
+ | driver.get("http://www.google.com") | ||
+ | |||
+ | # 2. Esperar hasta que aparezca la ventana de cookies y hacer clic en "Rechazar todo" | ||
+ | reject_cookies_button = WebDriverWait(driver, 10).until( | ||
+ | EC.element_to_be_clickable((By.XPATH, "//button[contains(., 'Rechazar todo')]")) | ||
+ | ) | ||
+ | reject_cookies_button.click() | ||
+ | |||
+ | # 3. Esperar hasta que el campo de búsqueda sea visible | ||
+ | search_box = WebDriverWait(driver, 10).until( | ||
+ | EC.visibility_of_element_located((By.NAME, "q")) | ||
+ | ) | ||
+ | |||
+ | # 4. Escribir "Selenium" en la barra de búsqueda y enviar el formulario | ||
+ | search_box.send_keys("Selenium") | ||
+ | search_box.send_keys(Keys.RETURN) | ||
+ | |||
+ | # 5. Esperar a que el título cambie y contenga "Selenium" | ||
+ | WebDriverWait(driver, 10).until(EC.title_contains("Selenium")) | ||
+ | print("¡Selenium está funcionando correctamente con Firefox!") | ||
+ | |||
+ | except Exception as e: | ||
+ | print(f"Error: {e}") | ||
+ | driver.save_screenshot("error_screenshot.png") | ||
+ | print("Captura de pantalla guardada como 'error_screenshot.png'") | ||
+ | |||
+ | finally: | ||
+ | # Cerrar el navegador | ||
+ | driver.quit() | ||
+ | |||
</syntaxhighlight> | </syntaxhighlight> | ||
¿Qué crees que va a ocurrir cuando ejecutemos esta prueba? | ¿Qué crees que va a ocurrir cuando ejecutemos esta prueba? | ||
− | Pues vamos a | + | Pues vamos a lanzarla y comprobemos qué ocurre: |
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
Línea 337: | Línea 417: | ||
from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||
from selenium.webdriver.common.keys import Keys | from selenium.webdriver.common.keys import Keys | ||
− | import | + | from webdriver_manager.chrome import ChromeDriverManager |
+ | from selenium.webdriver.chrome.service import Service | ||
+ | |||
+ | # Configurar Selenium para usar Chromium | ||
+ | options = webdriver.ChromeOptions() | ||
+ | options.add_argument('--no-sandbox') | ||
+ | options.add_argument('--disable-dev-shm-usage') | ||
− | + | # Usar webdriver-manager para gestionar el driver de Chromium | |
− | + | service = Service(ChromeDriverManager().install()) | |
− | + | driver = webdriver.Chrome(service=service, options=options) | |
− | |||
− | |||
− | |||
− | |||
− | + | try: | |
− | + | # Abre la aplicación web | |
driver.get("http://localhost:5000") | driver.get("http://localhost:5000") | ||
− | + | # Verifica que el título de la página es correcto | |
assert "Gestor de Tareas" in driver.title | assert "Gestor de Tareas" in driver.title | ||
− | + | # Buscar el campo de entrada de nueva tarea | |
input_field = driver.find_element(By.NAME, "title") | input_field = driver.find_element(By.NAME, "title") | ||
− | + | # Escribir en el campo de entrada | |
input_field.send_keys("Tarea de Selenium") | input_field.send_keys("Tarea de Selenium") | ||
input_field.send_keys(Keys.RETURN) | input_field.send_keys(Keys.RETURN) | ||
− | + | # Verificar que la tarea aparece en la lista | |
assert "Tarea de Selenium" in driver.page_source | assert "Tarea de Selenium" in driver.page_source | ||
+ | |||
+ | finally: | ||
+ | driver.quit() | ||
+ | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Línea 539: | Línea 625: | ||
¡Mucho ánimo! | ¡Mucho ánimo! | ||
+ | |||
+ | === Troubleshooting === | ||
+ | ==== Error THIRD_PARTY_NOTICES ==== | ||
+ | '''Notificado po'''r: Antonio Porcar (Grupo 3) | ||
+ | |||
+ | OSError: [Errno 8] Exec format error: '/home/user/.wdm/drivers/chromedriver/linux64/130.0.6723.58/chromedriver-linux64/THIRD_PARTY_NOTICES.chromedriver' | ||
+ | |||
+ | La solución consiste en reinstalar el paquete, desinstalandolo primero y borrando la carpeta que se genera, y después instalándolo otra vez. Son los comandos que siguen: | ||
+ | |||
+ | <syntaxhighlight lang="bash"> | ||
+ | $ pip uninstall webdriver-manager | ||
+ | $ rm ~/.wdm/ -r | ||
+ | $ pip install webdriver-manager | ||
+ | </syntaxhighlight> |
Revisión actual del 10:44 29 nov 2024
Contenido
- 1 Automatización de Pruebas de Software en una Aplicación Flask
- 1.1 Parte 1: creamos pruebas para una aplicación sencilla
- 1.1.1 Requisitos previos
- 1.1.2 Desarrollo de la Aplicación Flask
- 1.1.3 Pruebas Unitarias con pytest
- 1.1.4 Pruebas de cobertura con pytest-cov
- 1.1.5 Pruebas de interfaz con Selenium
- 1.1.6 Pruebas de Carga con Locust
- 1.2 Parte 2: Creamos pruebas para nuestra aplicación UVLHUB
- 1.3 Troubleshooting
- 1.1 Parte 1: creamos pruebas para una aplicación sencilla
Automatización de Pruebas de Software en una Aplicación Flask
Parte 1: creamos pruebas para una aplicación sencilla
El objetivo de la primera parte de la práctica es que nos familiaricemos con diferentes tipos de pruebas usando una aplicación web desarrollada con Flask. Durante la práctica se implementarán los siguientes tipos de pruebas:
- Pruebas unitarias con
pytest
para comprobar la funcionalidad interna de la aplicación. - Pruebas de cobertura para comprobar si nuestras pruebas tienen una buena cobertura de código.
- Pruebas de interfaz con
Selenium
para simular el comportamiento de un usuario interactuando con la interfaz web. - Pruebas de carga con
Locust
para evaluar el rendimiento de la aplicación bajo diferentes niveles de tráfico.
Requisitos previos
Antes de comenzar, asegúrate de tener instalados los siguientes paquetes y herramientas:
- Python 3
- Flask
-
pytest
para pruebas unitarias. -
pytest-cov
para la cobertura de código. -
Selenium
para pruebas de interfaz. -
Locust
para pruebas de carga. - El navegador chromium y chromium-driver (para Selenium).
Para instalar las dependencias necesarias (¡pero recuerda hacerlo en un entorno virtual!):
$ pip install flask pytest pytest-cov selenium locust webdriver-manager
Estructura del proyecto
flask_testing_project/
│
├── app.py # Archivo principal de la aplicación Flask
├── templates/ # Directorio que contiene la plantilla HTML
│ └── tasks.html # Plantilla para mostrar y agregar tareas
├── tests/
│ ├── test_app.py # Pruebas unitarias usando pytest
│ └── test_interfaz.py # Pruebas de interfaz con Selenium
└── locustfile.py # Archivo para pruebas de carga con Locust
Desarrollo de la Aplicación Flask
Vamos a crear una aplicación sencilla que gestione tareas. El usuario podrá ver las tareas en una interfaz web y agregar nuevas tareas utilizando un formulario. Además ofrece una API REST para la visualización y creación de tareas.
Código app.py
:
from flask import Flask, jsonify, request, render_template, redirect, url_for
app = Flask(__name__)
# Lista inicial de tareas (guardada en memoria)
tasks = [
{'id': 1, 'title': 'Comprar pan', 'done': False},
{'id': 2, 'title': 'Estudiar Python', 'done': False}
]
# Ruta para obtener la lista de tareas (versión HTML)
@app.route('/')
def task_list():
return render_template('tasks.html', tasks=tasks)
# Ruta para obtener la lista de tareas en JSON (API)
@app.route('/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
# Ruta para crear una nueva tarea desde un formulario HTML
@app.route('/add_task', methods=['POST'])
def add_task_html():
title = request.form.get('title')
if not title:
return "El título es necesario", 400
task = {
'id': tasks[-1]['id'] + 1 if tasks else 1,
'title': title,
'done': False
}
tasks.append(task)
return redirect(url_for('task_list'))
# Ruta para crear una nueva tarea (API JSON)
@app.route('/tasks', methods=['POST'])
def create_task():
if not request.json or 'title' not in request.json:
return jsonify({'error': 'El título es necesario'}), 400
task = {
'id': tasks[-1]['id'] + 1 if tasks else 1,
'title': request.json['title'],
'done': False
}
tasks.append(task)
return jsonify(task), 201
if __name__ == '__main__':
app.run(debug=True)
Como puedes ver en el código, por tanto, se ofrecen dos formas para interactuar con las tareas:
- Una página HTML que muestra la lista de tareas y un formulario para añadir nuevas tareas.
- Una API REST que devuelve la lista de tareas en formato JSON y permite agregar nuevas tareas mediante solicitudes POST.
Plantilla HTML
La plantilla tasks.html
es la encargada de mostrar las tareas y proporcionar un formulario para agregar nuevas tareas.
Archivo templates/tasks.html
:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gestor de Tareas</title>
</head>
<body>
<h1>Gestor de Tareas</h1>
<!-- Formulario para añadir una nueva tarea -->
<form action="{{ url_for('add_task_html') }}" method="POST">
<input type="text" name="title" placeholder="Añadir nueva tarea">
<button type="submit">Añadir tarea</button>
</form>
<!-- Lista de tareas -->
<h2>Lista de Tareas:</h2>
<ul>
{% for task in tasks %}
<li>{{ task.title }} {% if task.done %} (completada) {% endif %}</li>
{% endfor %}
</ul>
</body>
</html>
Lanza la aplicación
Veamos la aplicación en acción:
$ python app.py
Interactúa con ella creando y visualizando las tareas usando primero el formulario web y luego también mediante la API:
$ curl -X POST http://127.0.0.1:5000/tasks -H "Content-Type: application/json" \
-d '{"title": "Leer documentación de github actions"}'
$ curl http://127.0.0.1:5000/tasks
Pruebas Unitarias con pytest
Las pruebas unitarias se centrarán en probar los endpoints de la API de manera independiente.
Archivo tests/test_app.py
:
import pytest
from app import app
@pytest.fixture
def client():
with app.test_client() as client:
yield client
def test_get_tasks(client):
response = client.get('/tasks')
assert response.status_code == 200
assert 'Comprar pan' in response.get_data(as_text=True)
def test_create_task(client):
response = client.post('/tasks', json={'title': 'Aprender testing'})
assert response.status_code == 201
assert 'Aprender testing' in response.get_data(as_text=True)
def test_create_task_without_title(client):
response = client.post('/tasks', json={})
assert response.status_code == 400
data = response.get_json()
assert data['error'] == 'El título es necesario'
def test_task_list_updates(client):
response = client.post('/tasks', json={'title': 'Otra nueva tarea'})
assert response.status_code == 201
response = client.get('/tasks')
assert 'Otra nueva tarea' in response.get_data(as_text=True)
Lee el código de las pruebas e intenta deducir qué hace cada línea. En base a las pruebas manuales que has realizado antes, ¿crees que todas estas pruebas van a funcionar bien?
Ejecución de las pruebas:
$ pytest
Comprueba los resultados obtenidos. ¿Coinciden con lo que estabas esperando?
NOTA: Si recibes un error al lanzar pytest porque no se encuentra el módulo app, puedes intentarlo así (lo que añade el directorio actual (.) al PYTHONPATH):
$ PYTHONPATH=. pytest
Nota: En la segunda parte de la práctica, cuando lances las pruebas en UVLHUB, verás que esto no es necesario, ya que UVLHUB usa el archivo init.py en cada carpeta para que Python lo interprete como módulo y así cargarlo.
Pruebas de cobertura con pytest-cov
Para asegurarnos de que nuestras pruebas unitarias tienen una buena cobertura de código, vamos a utilizar pytest-cov
, una herramienta que extiende pytest
para generar un informe sobre qué porcentaje del código ha sido cubierto por las pruebas.
Y, ¿qué es la cobertura de código?
La cobertura de código mide el porcentaje de código ejecutado cuando se lanzan las pruebas. Esto nos ayuda a identificar las áreas de la aplicación que no están siendo probadas adecuadamente.
Medir la cobertura de las pruebas con pytest-cov
$ pytest --cov=app tests/
Este comando ejecutará todas las pruebas en la carpeta tests/ y generará un informe de cobertura que mostrará qué porcentaje del código en el archivo app.py fue ejecutado durante las pruebas.
Tras ejecutar la orden anterior deberías ver una salida del estilo de la siguiente:
------- coverage: xxx% ------- Name | Stmts | Miss | Cover -----------| ------|------|------ app.py | 26 | 8 | 69% TOTAL | 26 | 8 | 69%
Donde 'Stmts' indica el número total de sentencias en el archivo app.py; 'Miss' indica el número de sentencias que no fueron ejecutadas por las pruebas; y 'Cover' el porcentaje de cobertura de las pruebas sobre el archivo app.py.
También se puede obtener un informe más detallado con:
$ pytest --cov=app --cov-report=html tests/
Esta orden generará un informe HTML con detalles de qué líneas de código fueron ejecutadas durante las pruebas y cuáles no. El informe se generará en una carpeta llamada htmlcov/.
Para visualizar el informe, abre el archivo htmlcov/index.html en tu navegador:
$ xdg-open htmlcov/index.html
Con este informe podrás ver función a función cuál es su cobertura, y te permitirá identificar nuevas pruebas que añadir para verificar estos casos no cubiertos.
Pruebas de interfaz con Selenium
Estas pruebas simulan la interacción de un usuario con la interfaz web de la aplicación.
Importante: hay que tener instalados los siguientes paquetes y en este orden:
sudo dpkg -i <archivo deb oficial descargado> https://www.google.com/intl/es_es/chrome/?brand=FHFK&ds_kid=43700059038707842&gad_source=1&gclid=EAIaIQobChMIgKjcmdmSiQMV4jsGAB3_2AtqEAAYASAAEgJP2PD_BwE&gclsrc=aw.ds
sudo apt install chromium-browser
sudo apt install chromium-driver
Un pasito previo para ver Selenium
en acción
Antes de realizar las pruebas sobre nuestra aplicación vamos a hacer un pasito previo para comprobar que tenemos chromium y chromium-driver correctamente instalados y entender el tipo de cosas que podemos hacer con Selenium
.
Si no tienes Chrome instalado en tu equipo puedes simplemente instalar en tu sistema los paquetes chromium-brwoser
y chromium-driver
. Pero si ya tenías Chrome instalado tendrás que instalar el paquete webdriver-manager que se encargará de instalar las versiones adecuadas de Chromium y Chromium Webdriver.
pip install webdriver-manager
Archivo tests/test_selenium.py
:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
# Configurar Selenium para usar Chromium
options = webdriver.ChromeOptions()
# Quita '--headless' para ejecutar el navegador de manera visible
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
# Iniciar el driver de Chromium usando webdriver-manager
service = Service(ChromeDriverManager().install())
# Si obtienes un error al lanzar la prueba, puedes probar a cambiar la línea anterior por la siguiente
# service = Service('/usr/bin/chromedriver') # Pero tendrás que cerciorarte de la ruta exacta al driver en tu equipo
driver = webdriver.Chrome(service=service, options=options)
try:
# 1. Abrir Google
driver.get("https://www.google.com")
# 2. Esperar hasta que aparezca la ventana de cookies y hacer clic en "Rechazar todo"
reject_cookies_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//button[contains(., 'Rechazar todo')]"))
)
reject_cookies_button.click()
# 3. Esperar hasta que el campo de búsqueda sea visible
search_box = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.NAME, "q"))
)
# 4. Escribir "Selenium" en la barra de búsqueda y enviar el formulario
search_box.send_keys("Selenium")
search_box.send_keys(Keys.RETURN)
# 5. Esperar a que el título cambie y contenga "Selenium"
WebDriverWait(driver, 10).until(EC.title_contains("Selenium"))
print("¡Selenium está funcionando correctamente con Chromium!")
except Exception as e:
print(f"Error: {e}")
driver.save_screenshot("error_screenshot.png")
print("Captura de pantalla guardada como 'error_screenshot.png'")
finally:
# Cerrar el navegador
driver.quit()
Si prefieres usar Firefox como navegador para tus pruebas con Selenium, puedes usar el siguiente script proporcionado por Pablo Caballero del G3:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.firefox import GeckoDriverManager
from selenium.webdriver.firefox.service import Service
# Configurar Selenium para usar Firefox
options = webdriver.FirefoxOptions()
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
# Quitar "--headless" si quieres ver el navegador en modo visible
# options.add_argument("--headless") # Puedes quitar esto para modo visible
# Iniciar el driver de Firefox usando webdriver-manager
service = Service(GeckoDriverManager().install())
driver = webdriver.Firefox(service=service, options=options)
try:
# 1. Abrir Google
driver.get("http://www.google.com")
# 2. Esperar hasta que aparezca la ventana de cookies y hacer clic en "Rechazar todo"
reject_cookies_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//button[contains(., 'Rechazar todo')]"))
)
reject_cookies_button.click()
# 3. Esperar hasta que el campo de búsqueda sea visible
search_box = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.NAME, "q"))
)
# 4. Escribir "Selenium" en la barra de búsqueda y enviar el formulario
search_box.send_keys("Selenium")
search_box.send_keys(Keys.RETURN)
# 5. Esperar a que el título cambie y contenga "Selenium"
WebDriverWait(driver, 10).until(EC.title_contains("Selenium"))
print("¡Selenium está funcionando correctamente con Firefox!")
except Exception as e:
print(f"Error: {e}")
driver.save_screenshot("error_screenshot.png")
print("Captura de pantalla guardada como 'error_screenshot.png'")
finally:
# Cerrar el navegador
driver.quit()
¿Qué crees que va a ocurrir cuando ejecutemos esta prueba?
Pues vamos a lanzarla y comprobemos qué ocurre:
$ pytest -s tests/test_selenium.py
¿Has visto cómo se ha lanzado el navegador y ha ido realizando los pasos indicados en el archivo tests/test_selenium.py
? Pues ya tenemos todo listo para realizar las pruebas sobre nuestra aplicación.
Archivo tests/test_interfaz.py
:
Ahora sí, vamos a crear las pruebas de la vista para nuestra aplicación.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
# Configurar Selenium para usar Chromium
options = webdriver.ChromeOptions()
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
# Usar webdriver-manager para gestionar el driver de Chromium
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=options)
try:
# Abre la aplicación web
driver.get("http://localhost:5000")
# Verifica que el título de la página es correcto
assert "Gestor de Tareas" in driver.title
# Buscar el campo de entrada de nueva tarea
input_field = driver.find_element(By.NAME, "title")
# Escribir en el campo de entrada
input_field.send_keys("Tarea de Selenium")
input_field.send_keys(Keys.RETURN)
# Verificar que la tarea aparece en la lista
assert "Tarea de Selenium" in driver.page_source
finally:
driver.quit()
Lee el código de las pruebas e intenta deducir qué hace cada línea. En base a las pruebas manuales que has realizado antes, ¿crees que todas estas pruebas van a funcionar bien?
Ejecución de las pruebas de interfaz:
$ pytest -s tests/test_interfaz.py
Comprueba los resultados obtenidos. ¿Coinciden con lo que estabas esperando?
Selenium IDE
Y puede que estés pensando "sí, vale, las pruebas han funcionado como esperaba... pero si tuviera que escribir yo la prueba me costaría mucho trabajo".
Y es cierto, pero afortunadamente existe Selenium IDE
, que es una herramienta fácil de usar que nos permite grabar interacciones en el navegador y convertirlas en scripts de prueba que pueden ser ejecutados automáticamente.
Instalar Selenium IDE
Selenium IDE está disponible como una extensión para los navegadores Firefox y Chrome. Una vez instalada la extensión, haz clic en el ícono de Selenium IDE
en la barra de herramientas del navegador para abrirla.
Grabar una prueba con Selenium IDE
Iniciar una nueva grabación:
- Abre
Selenium IDE
.
- Selecciona
Create a new project
y dale un nombre a tu proyecto, por ejemplo, PruebasFlaskInterfaz.
- Introduce la URL de la aplicación Flask en ejecución.
Grabar la interacción:
- Haz clic en el botón de grabación en
Selenium IDE
.
- Acción 1: Abre la página principal de la aplicación Flask.
- Acción 2: En el formulario de tareas, escribe una nueva tarea, por ejemplo, "Tarea de Selenium IDE".
- Acción 3: Haz clic en el botón para añadir la tarea.
- Acción 4: Verifica que la nueva tarea aparece en la lista.
- Detén la grabación una vez que hayas completado estos pasos.
Guardar la prueba en Selenium IDE
.
Ejecutar la prueba grabada
En Selenium IDE
, selecciona la prueba grabada y haz clic en Run current test
.
Observa cómo Selenium IDE
reproduce automáticamente todas las acciones que realizaste durante la grabación (navegar, escribir en el formulario, etc.).
Exportar el test a código Selenium WebDriver
Exportar a Python:
- En
Selenium IDE
, selecciona el menúExport
y eligePython - pytest
.
- Selecciona la carpeta de pruebas y guárdalo como test_selenium_ide.py.
Ejecutar el test exportado:
Y ya puedes ejecutar el test exportado utilizando pytest:
$ pytest tests/test_selenium_ide.py
Esto ejecutará el test generado por Selenium IDE
en tu navegador usando Selenium WebDriver
.
Pruebas de Carga con Locust
Locust simulará múltiples usuarios accediendo a la aplicación simultáneamente, realizando operaciones como cargar la lista de tareas y agregar nuevas tareas.
Archivo locustfile.py
:
from locust import HttpUser, task, between
class WebsiteTestUser(HttpUser):
wait_time = between(1, 5)
@task(2)
def load_tasks(self):
print("Cargando la lista de tareas...")
response = self.client.get("/tasks")
if response.status_code == 200:
print("Lista de tareas cargada correctamente.")
else:
print(f"Error al cargar la lista de tareas: {response.status_code}")
@task(1)
def create_task(self):
print("Creando una nueva tarea...")
response = self.client.post("/tasks", json={"title": "Tarea generada por Locust"})
if response.status_code == 201:
print("Tarea creada correctamente.")
else:
print(f"Error al crear la tarea: {response.status_code}")
Ejecución de Locust
- Inicia la aplicación Flask si no estaba en ejecución:
$ python app.py
- Inicia Locust:
$ locust -f locustfile.py
- Abre la interfaz de Locust en tu navegador (
http://localhost:8089
) y configura el número de usuarios -por ejemplo, 10-, la tasa de generación (cada cuánto tiempo se lanza un nuevo usuario)-por ejemplo, 1- y el host sobre el que realizar las pruebas (http://localhost:5000
). Luego, inicia la prueba.
- En la terminal verás mensajes como estos hasta que se haya lanzado el número de clientes indicado:
Cargando la lista de tareas... Lista de tareas cargada correctamente. Creando una nueva tarea... Tarea creada correctamente. Creando una nueva tarea... Tarea creada correctamente. Cargando la lista de tareas... Lista de tareas cargada correctamente. Creando una nueva tarea... Tarea creada correctamente. Creando una nueva tarea... Tarea creada correctamente. [2024-10-07 17:35:02,798] hostname/INFO/locust.runners: All users spawned: {"WebsiteTestUser": 10} (10 total users)
Y además en la interfaz de Locust en tu navegador (http://localhost:8089
) puedes navegar por un informe interactivo con los resultados.
¿Cómo han ido las pruebas? ¿Ha aguantado el sistema esta carga?
Parte 2: Creamos pruebas para nuestra aplicación UVLHUB
Con lo que hemos aprendido hasta ahora, ya tenemos las herramientas necesarias para poder diseñar pruebas automatizadas para nuestra aplicación UVLHUB. Por ejemplo, ¿qué tipo de pruebas podrías desarrollar para probar la nueva funcionalidad de notepads que creaste en la primera práctica del curso?
No obstante, antes de lanzarte a ello quizá pueda ser buena idea echar un ojo a las pruebas ya existentes en el repositorio, que te pueden dar pistas, ideas y estrategias para diseñar las tuyas. Y al hacerlo verás que en UVLHUB se usa rosemary
, que facilita todavía más las tareas de testing: https://docs.uvlhub.io/rosemary/testing
.
Pero no te agobies por tener que aprender ahora algo nuevo como rosemary
, ya que si echas un ojo al código del repositorio vas a ver que, en realidad, para lanzar las pruebas rosemary
hace llamadas a pytest
. Su uso es totalmente opcional, aunque es cierto nos hace la vida un poquito más fácil.
Un ejemplo sencillo para ayudarte a arrancar
Vamos a desarrollar pruebas unitarias para el módulo notepad que preparamos en la Práctica 1 del curso. Pero en lugar de hacerlo desde cero, vamos a ver si podemos inspirarnos con algunos de los tests ya creados en el repositorio. Por ejemplo, echa un ojo a los tests del módulo profile: https://github.com/EGCETSII/uvlhub/blob/main/app/modules/profile/tests/test_unit.py
Fijate bien en la función test_edit_profile_page_get
.
Imagina que ahora quisiéramos crear un test para comprobar que el usuario de pruebas puede solicitar la lista de sus notepads, que inicialmente está vacía. ¿Podrías inspirarte en los tests del módulo profile para crear este otro? Sería muy similar, ¿verdad?
En el caso del notepad habría que hacer una petición get a /notepad
, luego habría que comprobar que el código de respuesta es 200 y finalmente comprobar que en los datos de la respuesta aparece el texto "You have no notepads." Algo así, por ejemplo:
def test_list_empty_notepad_get(test_client):
"""
Tests access to the empty notepad list via GET request.
"""
login_response = login(test_client, "user@example.com", "test1234")
assert login_response.status_code == 200, "Login was unsuccessful."
response = test_client.get("/notepad")
assert response.status_code == 200, "The notepad page could not be accessed."
assert b"You have no notepads." in response.data, "The expected content is not present on the page"
logout(test_client)
Partiendo de este ejemplo, ¿podrías ir diseñando las pruebas unitarias necesarias para comprobar todas las operaciones CRUD del módulo notepad?
¡Mucho ánimo!
Troubleshooting
Error THIRD_PARTY_NOTICES
Notificado por: Antonio Porcar (Grupo 3)
OSError: [Errno 8] Exec format error: '/home/user/.wdm/drivers/chromedriver/linux64/130.0.6723.58/chromedriver-linux64/THIRD_PARTY_NOTICES.chromedriver'
La solución consiste en reinstalar el paquete, desinstalandolo primero y borrando la carpeta que se genera, y después instalándolo otra vez. Son los comandos que siguen:
$ pip uninstall webdriver-manager
$ rm ~/.wdm/ -r
$ pip install webdriver-manager