Pruebas de las vistas 23-24
Los distintos módulos de decide [1] ofrecen su funcionalidad al usuario en las vistas. Estas, por tanto, también necesitan ser probadas. Sin embargo, esto puede llegar a ser complicado, ya que no deja de ser un servidor web que renderiza una vista. Veremos, en esta parte de la práctica, dos formas de realizar pruebas para las vistas en decide.
EJERCICIO 6: TEST DE VISTAS (ESTÁTICAS/API)
Una de las formas que conocemos para comunicarnos con una web exclusivamente mediante código son las API REST, de forma que realizamos peticiones a una URL, y, en lo que retorna como resultado, verificamos si está lo que debería. Por ejemplo. si hacemos un GET al módulo de voting, sabemos que debería retornar un objeto JSON que contiene la votación que esperaríamos que la vista renderizase.
Decide tiene implementada una clase para utilizar como base a la hora de implementar los test de interacción con la API, BaseTestCase. Esa clase nos ofrece un cliente para hacer llamadas REST, además de métodos de login y logout para simular el ingreso de un usuario en el sistema.
Vamos a replicar el mismo caso de prueba que hicimos previamente en el módulo voting, pero ahora, desde la vista:
def test_create_voting_API(self):
self.login()
data = {
'name': 'Example',
'desc': 'Description example',
'question': 'I want a ',
'question_opt': ['cat', 'dog', 'horse']
}
response = self.client.post('/voting/', data, format='json')
self.assertEqual(response.status_code, 201)
voting = Voting.objects.get(name='Example')
self.assertEqual(voting.desc, 'Description example')
Volviendo al informe de cobertura, podemos ver que la view de voting también tiene algunos missing. En concreto, no se está probando que cuando se actualice una votación (start, stop, tally), y no haya alguna acción en la request, la actualización falle. Añadamos un test para ello:
def test_update_voting_405(self):
v = self.create_voting()
data = {} #El campo action es requerido en la request
self.login()
response = self.client.post('/voting/{}/'.format(v.pk), data, format= 'json')
self.assertEquals(response.status_code, 405)
EJERCICIO 7: TEST DE VISTAS (DINÁMICAS/NAVEGACIÓN)
Si quisieramos probar aspectos de las vistas como el color, o que aparezca un gráfico de barras con el número de votantes, ya no nos sirve la respuesta de la API. Necesitamos renderizar las vistas. Selenium es un framework para testing y automatización de acciones web en general que nos permite simular como actuaría un usuario frente a nuestra aplicación.
Para nuestra primera toma de contacto con Selenium, podemos utilizar Selenium IDE, una extensión de Chrome que nos permite grabar el paso a paso de interacción con la vista.
Vamos realizar un test donde hagamos un login correcto. Descargaremos el script que genera Selenium de manera automática y analizaremos cada una de las instrucciones.
Ahora que ya estamos familiarizados con los tests de Selenium, vamos a escribir nuestra primera prueba. Para ello, crearemos authentication/test_selenium.py:
from django.test import TestCase
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from base.tests import BaseTestCase
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
class AdminTestCase(StaticLiveServerTestCase):
def setUp(self):
#Crea un usuario admin y otro no admin
self.base = BaseTestCase()
self.base.setUp()
#Opciones de Chrome
options = webdriver.ChromeOptions()
options.headless = True
self.driver = webdriver.Chrome(options=options)
super().setUp()
def tearDown(self):
super().tearDown()
self.driver.quit()
self.base.tearDown()
def test_simpleCorrectLogin(self):
#Abre la ruta del navegador
self.driver.get(f'{self.live_server_url}/admin/')
#Busca los elementos y “escribe”
self.driver.find_element(By.ID,'id_username').send_keys("admin")
self.driver.find_element(By.ID,'id_password').send_keys("qwerty",Keys.ENTER)
#Verifica que nos hemos logado porque aparece la barra de herramientas superior
self.assertTrue(len(self.driver.find_elements(By.ID, 'user-tools'))==1)
def test_simpleWrongLogin(self):
self.driver.get(f'{self.live_server_url}/admin/')
self.driver.find_element(By.ID,'id_username').send_keys("WRONG")
self.driver.find_element(By.ID,'id_password').send_keys("WRONG")
self.driver.find_element(By.ID,'login-form').submit()
#Si no, aparece este error
self.assertTrue(len(self.driver.find_elements(By.CLASS_NAME,'errornote'))==1)
time.sleep(5)
Nota: Ahora tomamos como base StaticLiveServerTestCase.
Se ha marcado la opcion headless para que Chrome no se abra visualmente y así pueda ser ejecutado en servidores sin interfaz gráfica. Si eliminamos esa línea, veremos como Chrome se abre y se navega por el sistema decide.
Volviendo al informe de cobertura, podemos ver como visualizer/view.py necesita más pruebas. Añadamos un nuevo caso de pruebas de navegación de interfaz para aquellas votaciones que aún no han comenzado:
from django.test import TestCase
from base.tests import BaseTestCase
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from voting.models import Voting, Question
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
class VisualizerTestCase(StaticLiveServerTestCase):
def setUp(self):
self.base = BaseTestCase()
self.base.setUp()
options = webdriver.ChromeOptions()
options.headless = True
self.driver = webdriver.Chrome(options=options)
super().setUp()
def tearDown(self):
super().tearDown()
self.driver.quit()
self.base.tearDown()
def test_simpleVisualizer(self):
q = Question(desc='test question')
q.save()
v = Voting(name='test voting', question=q)
v.save()
response =self.driver.get(f'{self.live_server_url}/visualizer/{v.pk}/')
vState= self.driver.find_element(By.TAG_NAME,"h2").text
self.assertTrue(vState, "Votación no comenzada")
EJERCICIO 8: VOTANDO EN LA CABINA
Dejamos como trabajo individual realizar una prueba de navegación que realice una votación en la cabina.
Nota para usuarios de WSL2: El compañero Joaquin Martín del grupo 1 comparte su experiencia para ejecutar los test de de la interfaz gráfica de decide con WSL2. A continuación, su paso a paso:
Todo esto se hace estando en el directorio $HOME.
1. Instalar version 114 de google-chrome:wget --no-verbose -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_114.0.5735.198-1_amd64.deb && sudo apt install -y /tmp/chrome.deb && rm /tmp/chrome.deb
curl -Lo chromedriver_linux64.zip "https://chromedriver.storage.googleapis.com/\
${chrome_driver}/chromedriver_linux64.zip"
3. Unzip y hacer chromedriver ejecutable:
mkdir -p "chromedriver/stable" && \
unzip -q "chromedriver_linux64.zip" -d "chromedriver/stable" && \
chmod +x "chromedriver/stable/chromedriver"
sudo cp ~/chromedriver/stable/chromedriver /usr/bin/
Para hacerlo he utilizado este blog como ayuda https://cloudbytes.dev/snippets/run-selenium-and-chrome-on-wsl2#step-3-install-compatible-chromedriver