web-dev-qa-db-fra.com

remplacer python variable locale de fonction dans unittest

J'ai une méthode en python (2.7) qui fait foo, et abandonne après 5 minutes si foo n'a pas fonctionné.

def keep_trying(self):
    timeout = 300  #empirically derived, appropriate timeout
    end_time = time.time() + timeout
    while (time.time() < end_time):
        result = self.foo()
        if (result == 'success'):
            break
        time.sleep(2)
    else:
        raise MyException('useful msg here')

Je connais certains résultats possibles de foo (), donc j'utilise mock pour truquer ces valeurs de retour. Le problème est que je ne veux pas que le test s'exécute 5 minutes avant de voir l'exception.

Existe-t-il un moyen de remplacer cette valeur locale de délai d'attente? J'aimerais que ce ne soit que quelques secondes pour que je puisse voir la boucle essayer plusieurs fois, puis abandonner et augmenter.

Ce qui suit ne fonctionne pas:

@patch.object(myClass.keep_trying, 'timeout')
@patch.object(myClass, 'foo')
def test_keep_trying(self, mock_foo, mock_timeout):
    mock_foo.return_value = 'failed'
    mock_timeout.return_value = 10 # raises AttributeError
    mock_timeout = 10 # raises AttributeError
    ...
22
anregen

Plutôt que d'essayer de simuler la valeur si timeout, vous voudrez simuler la valeur de retour de time.time().

par exemple.

@patch.object(time, 'time')
def test_keep_trying(self, mock_time):
    mock_time.side_effect = iter([100, 200, 300, 400, 500, 600, 700, 800])
    ...

Maintenant, la première fois que time.time() est appelée, vous obtiendrez la valeur 100, elle devrait donc expirer après quelques tours de votre boucle while. Vous pouvez également vous moquer de time.sleep Et compter le nombre de fois qu'il est appelé pour vous assurer qu'une partie du code fonctionne correctement.


Une autre approche (qui n'est pas complètement orthogonale à celle ci-dessus) consiste à permettre à l'utilisateur de passer un mot clé de temporisation facultatif à la fonction:

def keep_trying(self, timeout=300):
    ...

Cela vous permet de spécifier le timeout que vous voulez dans les tests (et dans le futur code qui ne veut pas attendre 5 minutes ;-).

16
mgilson

Vous ne pouvez pas vous moquer de la variable locale d'une fonction. Pour rendre votre code plus facile à tester, changez-le en, par exemple:

def keep_trying(self, timeout=300):
    end_time = time.time() + timeout
    # etc, as above

il devient donc trivial pour les tests de l'exécuter avec un délai d'attente plus court!

18
Alex Martelli