• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 4/25/22

Utilisez les fixtures

Je sais exactement ce que vous vous dites dans votre tête, que c’est encore un concept compliqué qu’il va falloir comprendre et apprendre. Mais détrompez-vous, dans ce chapitre vous allez voir que ce n’est pas très complexe à mettre en place, et que c’est très utile dans l’implémentation de vos tests.

Bon, c’est quoi les fixtures, alors ?

L'intérêt d’une fixture est de fournir un environnement de développement fixe afin de configurer un ensemble de tests avec un même contexte ou même jeu de données. C’est-à-dire que l’environnement créé par la fixture sera constant et identique pour chaque test qui l'appellera.

Pour faire simple, la fixture pourra contenir tout ce dont les tests ont besoin pour faire leur travail, et tout cela sera factorisé dans une seule et même fonction.

Peut-être que vous connaissez déjà quelques exemples d’utilisation des fixtures :

  • préparer des objets ;

  • démarrer ou arrêter des services ;

  • initialiser la base de données avec un jeu de données ;

  • créer le client de test pour projet web ;

  • configurer les mocks.

Ce n’est peut-être toujours pas assez clair, c’est normal, prenons un exemple précis !

Admettons que vous ayez créé un projet web à l’aide du framework Django, et que vous ayez mis en place plein de fonctionnalités qui nécessitent de créer un compte, et surtout d’être connecté avec ce même utilisateur. Jusqu'à maintenant, vous avez dû, pour chaque scénario, ajouter les lignes de code qui permettent la création et la connexion d’un utilisateur.

Avec les fixtures, tout cela est du passé !

En effet, la fixture regroupera tout le code nécessaire permettant de créer et de connecter un utilisateur sur l’application. Ainsi, chaque test qui nécessite un utilisateur connecté pourra l’appeler. Vous allez donc pouvoir simplifier la configuration du contexte de vos tests, et notamment gagner du temps lors de l’implémentation.

Maintenant que nous avons vu le rôle des fixtures, créons-les.

Créez une fixture

Il est relativement simple de créer une fixture. Effectivement, si vous savez créer une fonction, vous savez alors créer une fixture. Il faudra simplement faire un effort de plus et ajouter un décorateur au-dessus de la fonction. Merci Pytest de nous simplifier les choses. 😉

Suivez cet exemple d’une fixture qui construit un dictionnaire avec quelques données, et qui renvoie ce dictionnaire :

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "name" : "Gonnage"}
    return data

Les étapes :

  1. Importerpytest.

  2. Ajouter le décorateur :@pytest.fixture.

  3. Définir la fonction avec le nom de la fixture et les données nécessaires.

  4. Renvoyer l’ensemble des données.

Maintenant que vous savez créer une fixture, nous allons voir comment l’appeler.

Appelez une fixture

Reprenons la fixture que nous avons créée précédemment, et voyons comment déclarer notre test avec la fixturefirst_fixture:test_with_first_fixture(first_fixture).

Après avoir déclaré le nom du test, j’ai déclaré en argument la fixturefirst_fixturequi permet d'accéder aux données configurées dans cette fixture. Vous avez maintenant accès à toutes les données renvoyées par cette fixture.

En effet, une fois que vous allez exécuter le test, Pytest examinera les arguments et cherchera s’il existe une fixture ayant le même nom que ces paramètres en argument. Dans notre cas, il cherchera s’il existe une fixture qui a le nomfirst_fixtureet, si c’est le cas, il capturera la valeur renvoyée par la fixture et fournira l’objet en tant qu'argument de la fonction de test. 

Allez, c’est parti pour un petit exemple illustrant toute cette théorie :

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "name" : "Gonnage"}
    return data

def test_with_first_fixture(first_fixture):
    print(first_fixture)
    assert first_fixture["first_name"] == "Ranga"
    assert first_fixture["name"] == "Gonnage"

La fixture fournit un dictionnaire contenant deux éléments et peut être utilisé dans le test.
Appeler une fixture

Vous pouvez constater dans l’image ci-dessus que, lorsque le test est exécuté, l’argumentfirst_fixturecontient le dictionnaire renvoyé par la fixture. Ainsi, l’argument  first_fixtureest égal à  {'first_name': 'Ranga', 'name': 'Gonnage'}.

Voici un screencast qui vous présente ces étapes dans le détail, vous allez voir comment créer votre fixture et ensuite l’utiliser dans vos tests :

Réutilisez une fixture

Vous n’avez pas encore vu pourquoi il est intéressant de créer des fixtures. En effet, l’une des choses qui rendent la mise en place de fixture si puissante, c’est la possibilité de créer une configuration générique et de la réutiliser pour plusieurs tests. C’est-à-dire que vous pouvez appeler une même fixture pour deux tests différents et indépendants.

Par exemple, si nous prenons l’exemple précédent, nous pouvons créer un deuxième test qui appellera la fixturefirst_fixture:

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "name" : "Gonnage"}
    return data

def test_fixture(first_fixture):
    assert first_fixture["first_name"] == "Ranga"
    assert first_fixture["name"] == "Gonnage"

def test_fixture_bis(first_fixture):
    assert first_fixture["first_name"] == "Ranga"
    assert first_fixture["name"] == "Gonnage"

Dans le cas d’un projet utilisant le framework Flask ou Django, vous avez besoin d'instancier un client qui permette de créer un navigateur web de test. Vous pouvez donc tirer parti des fixtures pour faciliter la configuration du client. Ainsi, vous appellerez la fixture dans chacun de vos tests.

Par exemple, pour le framework Flask :

import pytest
from myapp import create_app

@pytest.fixture
def client():
    app = create_app({"TESTING": True})
    with app.test_client() as client:
        yield client

Et pour le framework Django :

import pytest
from django.test import Client

@pytest.fixture
def client():
    c = Client()
    return c 

Réutilisez une fixture dans une fixture

Un dernier point pour finaliser cette partie sur les fixtures. Dans certains scénarios de tests, il est possible que vous ayez besoin d’une donnée en plus ou d’une configuration différente pour valider le test.

Cependant, cette donnée peut être utile pour un seul test. Il n’est donc pas utile d’ajouter cette donnée dans la fixture principale, mais plutôt de créer une autre fixture qui appellera la fixture principale avec la donnée en plus.

Comme vous avez pu le remarquer, Pytest est un framework de test ultra flexible. Ainsi, Pytest permet d'appeler une fixture dans une fixture :

import pytest

@pytest.fixture
def first_fixture():
    data = {"first_name" : "Ranga",
            "name" : "Gonnage"}
    return data

@pytest.fixture
def second_fixture(first_fixture):
    first_fixture["email"] = "test@test.com"
    return first_fixture

def test_fixture(second_fixture):
    assert second_fixture["first_name"] == "Ranga"
    assert second_fixture["name"] == "Gonnage"
    assert second_fixture["email"] == "test@test.com"

La deuxième fixturesecond_fixturecomplète la première fixturefirst_fixtureavec un nouvel élément dans le dictionnaire. Effectivement, dans cette seconde fixture nous ajoutons un élément qui a pour cléemailet pour valeurtest@test.com.

Le testtest_fixturedémontre que nous avons bien accès à l’élément qui a pour cléemail. N’hésitez pas à essayer ce bout de code, vous constaterez que le test passe bien.

À vous de jouer !

Allez, c’est bientôt fini pour les fixtures : reprenez votre projet Django (OC-Commerce), sur lequel nous travaillerons pour la suite du cours.

Votre mission :

  • Ajoutez des fixtures pour simplifier vos tests.

Vous pourrez par exemple configurer un utilisateur connecté, configurer les mocks, ou même initialiser la base de données.

Retrouvez une proposition de correction sur GitHub !

En résumé

  • La création d’une fixture permet d’avoir un environnement bien connu et fixe dans lequel les tests sont exécutés, afin que les résultats soient reproductibles.

  • Le décorateur@pytest.fixturepermet de convertir une simple fonction en une fixture.

  • Pour utiliser une fixture dans un test, elle doit être définie en argument du test, et elle peut être utilisée autant de fois que nécessaire.

  • Une fixture peut en appeler une autre pour compléter la configuration.

Maintenant que vous connaissez le concept de fixture, vous savez configurer un test unitaire en évitant la duplication de code. Néanmoins, ce n’est pas la seule solution qui existe, nous allons voir comment organiser les tests en classes afin de simplifier l’implémentation des tests.

Example of certificate of achievement
Example of certificate of achievement