Gitlab

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    
Top