Een globale variabele belachelijk maken

Ik heb geprobeerd een aantal unit-tests voor een module te implementeren. Een voorbeeldmodule met de naam alphabet.pyis als volgt:

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

Ik zou de reactie van een database willen bespotten met enkele waarden naar keuze, maar de onderstaande code lijkt niet te werken.

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

Ik heb veel voorbeelden gezien waarin ‘patch’ wordt toegepast op methoden en klassen, maar niet op variabelen. Ik geef er de voorkeur aan om de methode database.getniet te patchen, omdat ik deze later opnieuw kan gebruiken met andere parameters, dus ik zou een ander antwoord nodig hebben.

Wat doe ik hier verkeerd?


Antwoord 1, autoriteit 100%

Variabelen kunnen als volgt worden gepatcht:

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

Bijvoorbeeld:

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

Antwoord 2, autoriteit 56%

Probeer dit:

import unittests  
import alphabet   
from unittest import mock 
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'))

Je moet de mock toepassen terwijl de afzonderlijke tests daadwerkelijk worden uitgevoerd, niet alleen in setUp(). We kunnen de mock makenin setUp()en later toepassen met een with ...Context Manager.


Antwoord 3, autoriteit 2%

Ik kwam een ​​probleem tegen waarbij ik variabelen probeerde te spotten die buiten een functie of klasse werden gebruikt, wat problematisch is omdat ze worden gebruikt op het moment dat je de klasse probeert te spotten, voordat je de waarden kunt spotten.

Ik heb uiteindelijk een omgevingsvariabele gebruikt. Als de omgevingsvariabele bestaat, gebruikt u die waarde en gebruikt u anders de toepassingsstandaard. Op deze manier kon ik de waarde van de omgevingsvariabele in mijn tests instellen.

In mijn test had ik deze code voordat de klas werd geïmporteerd

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

In mijn klas:

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

Mijn config.LOG_PATHis standaard /var/log/<my app name>, maar wanneer de test nu loopt, is het logpad ingesteld op de huidige map. Op deze manier heb je geen root-toegang nodig om de tests uit te voeren.


Antwoord 4

Je hoeft geen mock te gebruiken. Importeer gewoon de module en wijzig de waarde van de global binnen setUp():

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

Other episodes