2017-10-16 4 views
1

Donc, ce que je voudrais réaliser, c'est de déranger automatiquement les fonctions de divers modules avec pytest. Donc, je définissais dans mon conftest.py:Comment ajouter dynamiquement de nouveaux appareils à un test basé sur la signature de l'appareil d'un test

import sys 
import __builtin__ 
from itertools import chain 


# Fixture factory magic START 
NORMAL_MOCKS = [ 
    "logger", "error", "logging", "base_error", "partial"] 
BUILTIN_MOCKS = ["exit"] 


def _mock_factory(name, builtin): 
    def _mock(monkeypatch, request): 
     module = __builtin__ if builtin else request.node.module.MODULE 
     ret = Mock() 
     monkeypatch.setattr(module, name, ret) 
     return ret 
    return _mock 


iterable = chain(
    ((el, False) for el in NORMAL_MOCKS), 
    ((el, True) for el in BUILTIN_MOCKS)) 
for name, builtin in iterable: 
    fname = "mock_{name}".format(name=name) 
    _tmp_fn = pytest.fixture(name=fname)(_mock_factory(name, builtin)) 
    _tmp_fn.__name__ = fname 
    setattr(
     sys.modules[__name__], 
     "mock_{name}".format(name=name), _tmp_fn) 
# Fixture normal factory magic END 

Cela fonctionne et tout, mais je voudrais d'omettre l'utilisation des listes NORMAL_MOCKS et BUILTIN_MOCKS. Donc, fondamentalement, dans un crochet pytest je devrais être capable de voir que dire il ya un appareil mock_foo, mais ce n'est pas encore enregistré, alors je crée un faux pour cela avec l'usine et l'enregistrer. Je ne pouvais pas comprendre comment faire ça. Fondamentalement, je regardais dans la fonction pytest_runtest_setup, mais je ne pouvais pas comprendre comment faire l'enregistrement de l'appareil. Donc, fondamentalement, je voudrais savoir avec quel hook/appel puis-je enregistrer de nouvelles fonctions de fixation par programmation de ce crochet.

+0

La solution finale basée sur les conseils de @ sergey-vasilyev peut être trouvée ici -> https://gist.github.com/dpapp-hortonworks/6224068ffc11d18c500b75a861941dfb –

Répondre

0

Une des façons est de paramétrer les essais au stade collection/génération, à savoir avant l'exécution de test commence: https://docs.pytest.org/en/latest/example/parametrize.html

# conftest.py 
import pytest 

def mock_factory(name): 
    return name 

def pytest_generate_tests(metafunc): 
    for name in metafunc.fixturenames: 
     if name.startswith('mock_'): 
      metafunc.parametrize(name, [mock_factory(name[5:])]) 

# test_me.py 
def test_me(request, mock_it): 
    print(mock_it) 

Une solution très simple. Mais l'inconvénient est que le test est signalé comme paramétrés quand il est en fait pas:

$ pytest -s -v -ra 
====== test session starts ====== 

test_me.py::test_me[it] PASSED 

====== 1 passed in 0.01 seconds ====== 

Pour simuler entièrement la fonction args sans la paramétrisation, vous pouvez faire un tour moins évident:

# conftest.py 
import pytest 

def mock_factory(name): 
    return name 

@pytest.hookimpl(hookwrapper=True) 
def pytest_runtest_protocol(item, nextitem): 
    for name in item.fixturenames: 
     if name.startswith('mock_') and name not in item.funcargs: 
      item.funcargs[name] = mock_factory(name[5:]) 
    yield 

Le crochet pytest_runtest_setup est aussi un bon endroit pour cela, aussi longtemps que je viens d'essayer.

Notez que vous n'inscrivez pas l'appareil dans ce cas. Il est trop tard pour l'enregistrement des appareils, car tous les appareils sont rassemblés et préparés beaucoup plus tôt aux étapes de collecte/paramétrisation. A ce stade, vous ne pouvez exécuter que les tests et fournir les valeurs. Il est de votre responsabilité de calculer les valeurs de fixture et de les détruire par la suite.

+0

C'est une très bonne solution. Merci (je ne peux pas mettre en ligne ce compte en raison d'une mauvaise réputation, je vais voter avec mon compte normal). Est-il possible d'accéder à l'objet 'request' dans la fonction' pytest_generate_tests'. Je voudrais simuler des fonctions dans un module spécifique que je définis dans une variable dans un module de test. –

+0

J'ai réalisé que le https://docs.pytest.org/en/latest/parametrize.html#the-metafunc-object a un membre de module qui est ce dont j'ai besoin de l'objet de requête. Merci pour l'aide! –