Dans pytest, à quoi sert les fichiers conftest.py?

218

J'ai découvert récemment pytest. Ça a l'air génial. Cependant, je pense que la documentation pourrait être meilleure.

J'essaie de comprendre à quoi conftest.pyservent les fichiers.

Dans ma suite de tests (actuellement petite), j'ai un conftest.pyfichier à la racine du projet. Je l'utilise pour définir les fixations que j'injecte dans mes tests.

J'ai deux questions:

  1. Est-ce la bonne utilisation de conftest.py? A-t-il d'autres utilisations?
  2. Puis-je avoir plus d'un conftest.pyfichier? Quand voudrais-je faire ça? Des exemples seront appréciés.

Plus généralement, comment définiriez-vous le but et l'utilisation correcte des conftest.pyfichiers dans une suite de tests py.test?

Aviv Cohn
la source
9
Vous m'avez eu àIt seems great. However, I feel the documentation could be better.
user9074332
7
Oui, la documentation pourrait être tellement meilleure. J'ai cherché dans toute la documentation de pytest conftest.pyet bien qu'il existe de nombreuses références à faire cette chose ou à faire cette chose avec un fichier conftest, nulle part dans la documentation cela n'indique jamais que lorsque pytest teste la découverte, tous les fichiers conftest.py trouvés (dans le le répertoire strucutre sur lequel la découverte de test est effectuée) sera exécuté pendant la phase de collecte des tests (avant l'exécution des tests). J'ai dû comprendre cela moi-même par l'expérimentation.
Daniel Goldfarb

Réponses:

290

Est-ce l'utilisation correcte de conftest.py?

Oui, ça l'est. Les luminaires sont une utilisation potentielle et courante de conftest.py. Les fixtures que vous définirez seront partagées entre tous les tests de votre suite de tests. Cependant, la définition de fixtures à la racine conftest.pypeut être inutile et ralentirait les tests si ces fixtures n'étaient pas utilisées par tous les tests.

A-t-il d'autres utilisations?

Oui.

  • Fixtures : définissez les fixtures pour les données statiques utilisées par les tests. Ces données sont accessibles par tous les tests de la suite, sauf indication contraire. Cela pourrait être des données ainsi que des aides de modules qui seront transmises à tous les tests.

  • Chargement de plugins externes : conftest.pypermet d'importer des plugins ou modules externes. En définissant la variable globale suivante, pytest chargera le module et le rendra disponible pour son test. Les plugins sont généralement des fichiers définis dans votre projet ou d'autres modules qui pourraient être nécessaires dans vos tests. Vous pouvez également charger un ensemble de plugins prédéfinis comme expliqué ici .

    pytest_plugins = "someapp.someplugin"

  • Crochets : vous pouvez spécifier des crochets tels que les méthodes de configuration et de démontage et bien plus encore pour améliorer vos tests. Pour un ensemble de crochets disponibles, lisez ici . Exemple:

    def pytest_runtest_setup(item):
         """ called before ``pytest_runtest_call(item). """
         #do some stuff`
  • Tester le chemin racine : c'est un peu une fonction cachée. En définissant conftest.pydans votre chemin racine, vous aurezpytest reconnaître vos modules d'application sans spécifier PYTHONPATH. En arrière-plan, py.test modifie votre sys.pathen incluant tous les sous-modules trouvés depuis le chemin racine.

Puis-je avoir plus d'un fichier conftest.py?

Oui, vous le pouvez et il est fortement recommandé si votre structure de test est quelque peu complexe. conftest.pyles fichiers ont une portée de répertoire. Par conséquent, la création d'appareils et d'aides ciblés est une bonne pratique.

Quand voudrais-je faire ça? Des exemples seront appréciés.

Plusieurs cas pourraient convenir:

Création d'un ensemble d'outils ou de crochets pour un groupe particulier de tests.

root / mod / conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff


test root/mod2/test.py will NOT produce "I am mod"

Chargement d'un ensemble d' appareils pour certains tests mais pas pour d'autres.

root / mod / conftest.py

@pytest.fixture()
def fixture():
    return "some stuff"

root / mod2 / conftest.py

@pytest.fixture()
def fixture():
    return "some other stuff"

root / mod2 / test.py

def test(fixture):
    print(fixture)

Imprime "quelques autres trucs".

Remplacer les crochets hérités de la racine conftest.py.

root / mod / conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff

root / conftest.py

def pytest_runtest_setup(item):
    print("I am root")
    #do some stuff

En exécutant un test à l'intérieur root/mod, seul "I am mod" est imprimé.

Vous pouvez en savoir plus conftest.py ici .

ÉDITER:

Que se passe-t-il si j'ai besoin que des fonctions d'assistance simples soient appelées à partir d'un certain nombre de tests dans différents modules - seront-elles disponibles si je les place dans un fichier conftest.py? Ou devrais-je simplement les mettre dans un module helpers.py et les importer et les utiliser dans mes modules de test?

Vous pouvez utiliser conftest.pypour définir vos assistants. Cependant, vous devez suivre la pratique courante. Les assistants peuvent être utilisés comme appareils au moins danspytest . Par exemple, dans mes tests, j'ai un simulateur d'aide redis que j'injecte dans mes tests de cette façon.

root / helper / redis / redis.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root / tests / stuff / conftest.py

pytest_plugin="helper.redis.redis"

root / tests / stuff / test.py

def test(mock_redis):
    print(mock_redis.get('stuff'))

Ce sera un module de test que vous pourrez importer librement dans vos tests. NOTEZ que vous pourriez potentiellement nommer redis.pycomme conftest.pysi votre module rediscontient plus de tests. Cependant, cette pratique est découragée en raison de l'ambiguïté.

Si vous souhaitez l'utiliser conftest.py, vous pouvez simplement mettre cet assistant dans votre racine conftest.pyet l'injecter en cas de besoin.

root / tests / conftest.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root / tests / stuff / test.py

def test(mock_redis):
    print(mock_redis.get(stuff))

Une autre chose que vous pouvez faire est d'écrire un plugin installable. Dans ce cas, votre assistant peut être écrit n'importe où, mais il doit définir un point d'entrée à installer dans votre et les autres cadres de test potentiels. Voir ça .

Si vous ne souhaitez pas utiliser de fixtures, vous pouvez bien sûr définir un assistant simple et utiliser simplement la vieille importation partout où cela est nécessaire.

root / tests / helper / redis.py

class MockRedis():
    # stuff

root / tests / stuff / test.py

from helper.redis import MockRedis

def test():
    print(MockRedis().get(stuff))

Cependant, ici, vous pourriez avoir des problèmes avec le chemin, car le module ne se trouve pas dans un dossier enfant du test. Vous devriez pouvoir surmonter cela (non testé) en ajoutant un __init__.pyà votre assistant

root / tests / helper / __ init__.py

from .redis import MockRedis

Ou ajoutez simplement le module d'assistance à votre PYTHONPATH.

Simone Zandara
la source
Je rencontre une situation dans laquelle mon premier module de test unitaire test_aaaaa.pytente réellement de s'exécuter avant que la configuration de l'appareil ne se termine conftest.py. Avez-vous une idée de la raison pour laquelle cela peut se produire?
user9074332
Sans conftest.py, votre chemin racine de test n'est pas ajouté au sys.path et vous verrez des erreurs comme "ModuleNotFoundError: aucun module nommé 'foobar'".
Cale Sweeney
11

Au sens large, conftest.py est un plugin local par répertoire. Vous définissez ici les crochets et les accessoires spécifiques au répertoire. Dans mon cas, j'ai un répertoire racine contenant des répertoires de tests spécifiques au projet. Une magie commune est placée dans conftest.py «racine». Spécifique au projet - dans les leurs. Je ne vois rien de mal à stocker des appareils dans conftest.py à moins qu'ils ne soient pas largement utilisés (dans ce cas, je préfère les définir directement dans les fichiers de test)

Canard furieux
la source
10

J'utilise le conftest.pyfichier pour définir les fixations que j'injecte dans mes tests, est-ce la bonne utilisation de conftest.py?

Oui , un appareil est généralement utilisé pour préparer les données pour plusieurs tests.

A-t-il d'autres utilisations?

Oui , un appareil est une fonction qui est exécutée pytestavant et parfois après les fonctions de test réelles. Le code dans le luminaire peut faire ce que vous voulez. Par exemple, un appareil peut être utilisé pour obtenir un ensemble de données sur lequel les tests doivent fonctionner, ou un appareil peut également être utilisé pour mettre un système dans un état connu avant d'exécuter un test.

Puis-je avoir plus d'un conftest.pyfichier? Quand voudrais-je faire ça?

Tout d'abord, il est possible de placer les appareils dans des fichiers de test individuels. Cependant, pour partager des appareils entre plusieurs fichiers de test, vous devez utiliser un conftest.pyfichier quelque part situé au centre pour tous les tests. Les appareils peuvent être partagés par n'importe quel test. Ils peuvent être placés dans des fichiers de test individuels si vous souhaitez que l'appareil ne soit utilisé que par des tests dans ce fichier.

Deuxièmement, oui , vous pouvez avoir d'autres conftest.pyfichiers dans les sous-répertoires du répertoire de tests supérieur. Si vous le faites, les appareils définis dans ces niveaux inférieursconftest.py fichiers de seront disponibles pour les tests dans ce répertoire et ces sous-répertoires.

Enfin, placer les appareils dans le conftest.pyfichier à la racine du test les rendra disponibles dans tous les fichiers de test.

lmiguelvargasf
la source