Tests fonctionnels
Pour une application web conteneurisée, il est fortement recommandé de vérifier qu’elle répond au moins sur le code http 200 sur sa home. Pour lancer des tests fonctionnels, on peut utiliser le docker-compose.yml
.
Tester l’application dans la CI avec curl
Un des problèmes à résoudre est que Docker lance un service, mais il ne sait pas à quel moment il est disponible. Les images de base de données ont souvent un premier démarrage lent, le temps de créer les bases et utilisateurs, voir même de charger des données. Voici un exemple simple, avec un service web et sa base de données qui vérifie le bon fonctionnement du service web avant de lancer un appel à l’application:
# compose command to merge production file and and dev/tools overrides
COMPOSE?=docker-compose -f docker-compose.yml -f tools.yml -f dev.yml
export COMPOSE
test-app:
# run compose in background
$(COMPOSE) up -d
# wait for services using the bearstech/traefik-dev container utils
$(COMPOSE) exec -T traefik wait_for_services -v --timeout 60
# ensure selenium container is aware of traefik hosts
$(COMPOSE) exec -T traefik traefik_hosts > traefik_hosts
# show apps logs
$(COMPOSE) logs --tail="all" app
# test our app!
curl --verbose --fail --retry-delay 3 --retry 3 \
--header 'Host: $(CI_ENVIRONMENT_DOMAIN)' \
'http://127.0.0.1:$(TRAEFIK_HTTP_PORT)/'
# stop compose
$(COMPOSE) down --remove-orphans
Dans l’exemple, l’enchaînement des commandes permet un test fonctionnel de l’application conteneurisée:
- Les services sont lancés puis détachés
- Attente que le service db, un mysql, réponde
- Appel du service web ou de tests fonctionnels
- Les services sont arretés, les containers sont détruits
Tests fonctionnels avec pytest
A la place de curl, on peut rédiger un test pour notre application en python qui permet de valider aussi le contenu d’une page.
import pytest
from app import web
def test_status():
app = web.create_app()
with app.test_client() as client:
response = client.get('/status')
assert response.status_code == 200
assert b"true" in response.data
Ici, le test utilise la méthode test_client
de Flask Werkzeug’s client afin de vérifier que le serveur répond bien sur l’url indiquée, et qu’on reçoit la bonne information.
La commande $(COMPOSE) exec -T app pytest -vx tests/
permet de lancer le test dans la CI.
Aller plus loin avec Selenium
La librairie Selenium implémente un WebDriver qui émule un driver spécifique pour vos browsers favoris, et qui permet de réaliser des tests poussés.
Voici un exemple qui implémente un test avec Selenium pour notre application conteneurisée en Python:
Simuler un appel distant
Dans l’application, rédiger un test qui viendra utiliser le conteneur du service ‘firefox’ pour analyser une page de notre application:
import os
import json
import pytest
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
@pytest.fixture(scope='session')
def driver(request):
d = webdriver.Remote(
command_executor='http://firefox:4444/wd/hub',
desired_capabilities=DesiredCapabilities.FIREFOX,
)
yield d
d.close()
def test_index(driver):
driver.get('http://{CI_ENVIRONMENT_DOMAIN}/'.format(**os.environ))
WebDriverWait(driver, 20).until(EC.title_is('Bearstech'))
assert 'Bearstech' in driver.title
Ici, on vient analyser le contenu de la balise title
de l’application lancée dans son propre conteneur, comme si un véritable navigateur le faisait.
Ajouter un service Selenium
Dans le docker-compose, ajouter un service qui utilise l’image Docker du WebDriver Firefox et le lier au reverse-proxy:
# we can use selenium remote webdriver to test our app
# see https://github.com/SeleniumHQ/docker-selenium
firefox:
image: selenium/standalone-firefox
volumes:
- ./traefik_hosts:/etc/hosts
expose: ["4444"]
labels:
traefik.frontend.rule: Host:selenium.myapp
traefik.enable: 'true'
traefik.tags: web
depends_on:
- traefik
Instancier le test fonctionnel dans la CI
Enfin, écrire la commande dans le Makefile qui déclenchera le test dans la CI:
test_application:
# run tests/ using pytest
$(COMPOSE) exec -T app pytest -vx -p no:cacheprovider tests/
# run our tests.
test: up test_application
Dans le fichier .gitlab-ci.yml
on peut dès lors ajouter une étape de tests POST build qui intervient avant le push de l’image consolidée sur la registry:
stages:
- pull # pull latest images required for our build
- build # build the image
- test # run the tests
- down # always make compose down
- push # push the image to the local registry
[...]
test:
stage: test
script:
# run our test
- make test
down:
stage: down
script:
- make down
when: always