Burlándose de una variable global

He estado intentando implementar algunas pruebas unitarias para un módulo. Un módulo de ejemplo llamado alphabet.py es el siguiente:

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 

Me gustaría burlarme de la respuesta de una base de datos con algunos valores de mi elección, pero el código a continuación no parece funcionar.

 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')) 

He visto muchos ejemplos en los que el ‘parche’ se aplica a métodos y clases, pero no a variables. Prefiero no parchear el método database.get porque puedo usarlo con diferentes parámetros más adelante, por lo que necesitaría una respuesta diferente.

¿Qué estoy haciendo mal aquí?

No necesitas usar el simulacro. Solo importa el módulo y altera el valor del global dentro de setUp() :

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

Prueba esto:

 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')) 

setUp() aplicar el simulacro mientras se ejecutan las pruebas individuales, no solo en setUp() . Podemos crear el simulacro en setUp() y aplicarlo más tarde con un with ... Context Manager.

Las variables se pueden parchear de la siguiente manera:

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

Por ejemplo:

 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') 

Me encontré con un problema en el que intentaba simular las variables que se usaron fuera de cualquier función o clase, lo cual es problemático porque se usan en el momento en que intentas burlarte de la clase, antes de que puedas simular los valores.

Terminé usando una variable de entorno. Si la variable de entorno existe, use ese valor, de lo contrario use el valor predeterminado de la aplicación. De esta manera podría establecer el valor de la variable de entorno en mis pruebas.

En mi prueba, tenía este código antes de que se importara la clase

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

En mi clase:

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

Por defecto, mi config.LOG_PATH es /var/log/ , pero ahora, cuando la prueba se está ejecutando, la ruta del registro se establece en el directorio actual. De esta manera no necesita acceso de root para ejecutar las pruebas.