web-dev-qa-db-fra.com

Se moquer d'une variable globale

J'ai essayé d'implémenter des tests unitaires pour un module. Un exemple de module nommé alphabet.py est le suivant:

import database

def length_letters():
    return len(letters)

def contains_letter(letter):
    return True if letter in letters else False


letters = database.get('letters')   # returns a list of letters

Je voudrais me moquer de la réponse d'une base de données avec certaines valeurs de mon choix, mais le code ci-dessous ne semble pas fonctionner.

import unittests  
import alphabet   
from unittest.mock import patch   


class TestAlphabet(unittest.TestCase): 
    @patch('alphabet.letters')
    def setUp(self, mock_letters):
        mock_letters.return_value = ['a', 'b', 'c']   

    def test_length_letters(self):
        self.assertEqual(3, alphabet.length_letters())

    def test_contains_letter(self):   
        self.assertTrue(alphabet.contains_letter('a'))

J'ai vu de nombreux exemples dans lesquels "patch" est appliqué aux méthodes et aux classes, mais pas aux variables. Je préfère ne pas appliquer de correctif à la méthode database.get car je pourrais l’utiliser ultérieurement avec des paramètres différents, j’aurais donc besoin d’une réponse différente.

Qu'est-ce que je fais mal ici?

14
Funkatic

Vous n'avez pas besoin de vous moquer. Importez simplement le module et modifiez la valeur du global dans setUp():

import alphabet

class TestAlphabet(unittest.TestCase): 
   def setUp(self):
        alphabet.letters = ['a', 'b', 'c']
2
John Gordon

Essaye ça:

import unittests  
import alphabet   
from unittest.mock import patch   


class TestAlphabet(unittest.TestCase): 
    def setUp(self):
        self.mock_letters = mock.patch.object(
            alphabet, 'letters', return_value=['a', 'b', 'c']
        )

    def test_length_letters(self):
        with self.mock_letters:
            self.assertEqual(3, alphabet.length_letters())

    def test_contains_letter(self):
        with self.mock_letters:
            self.assertTrue(alphabet.contains_letter('a'))

Vous devez appliquer le modèle pendant que les tests individuels sont en cours d'exécution, pas seulement dans setUp(). Nous pouvons créer la maquette dans setUp() et l’appliquer ultérieurement avec un with ... Gestionnaire de contexte.

21
Will

Les variables peuvent être corrigées par (Python2) 

from mock import patch
@patch('module.variable', new_value)    

Par exemple:

import alphabet
from mock import patch
@patch('alphabet.letters', ['a', 'b', 'c'])
class TestAlphabet():

    def test_length_letters(self):
        assert 3 == alphabet.length_letters()

    def test_contains_letter(self):
       assert alphabet.contains_letter('a')

Cela devrait également fonctionner pour Python3, je suppose, puisque mock est un backport de unittest.mock

6
Valera Maniuk

J'ai rencontré un problème en essayant de simuler des variables utilisées en dehors de toute fonction ou classe, ce qui est problématique car elles sont utilisées dès que vous essayez de vous moquer de la classe, avant de pouvoir vous moquer des valeurs.

J'ai fini par utiliser une variable d'environnement. Si la variable d'environnement existe, utilisez cette valeur, sinon utilisez la valeur par défaut de l'application. De cette façon, je pourrais définir la valeur de la variable d'environnement dans mes tests.

Dans mon test, j'avais ce code avant l'importation de la classe

os.environ["PROFILER_LOG_PATH"] = "./"

Dans ma classe:

log_path = os.environ.get("PROFILER_LOG_PATH",config.LOG_PATH)

Par défaut, mon config.LOG_PATH est /var/log/<my app name>, mais maintenant, lorsque le test est en cours d'exécution, le chemin du journal est défini sur le répertoire actuel. De cette façon, vous n'avez pas besoin d'un accès root pour exécuter les tests.

1
Ruth Grace Wong