2016-07-12 110 views
7

Ho cercato di implementare alcuni test unitari per un modulo. Un modulo di esempio di nome alphabet.py è la seguente:Mocking una variabile globale

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 

mi piacerebbe prendere in giro la risposta da un database con alcuni valori della mia scelta, ma il codice qui sotto non sembra funzionare.

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

Ho visto molti esempi in cui 'patch' è applicata a metodi e classi, ma non a variabili. Preferisco non applicare la patch al metodo database.get perché potrei utilizzarlo di nuovo con parametri diversi in seguito, quindi avrei bisogno di una risposta diversa.

Cosa sto facendo di sbagliato qui?

risposta

2

Non è necessario utilizzare il mock. Basta importare il modulo e modificare il valore del globale all'interno setUp():

import alphabet 

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

Una sfortunata conseguenza di questo approccio è che qualsiasi altro test che utilizza questa variabile di livello modulo avrà esito negativo a meno che non si memorizzi il vecchio valore e lo si reinserisca. Mocking si prende cura di questo per te. –

+1

È possibile impostare il valore di 'alphabet.letters' su ciò che era nella funzione' tearDown'. – tomas

+0

Inoltre, poiché 'setUp' ha un ambito per l'intera classe di test, puoi usare solo questo valore per' letters'. La risposta di Will qui sotto ti consente di fare più simulazioni per diversi casi di test, e si ripuliscono alla fine, quindi non c'è rischio di inquinamento da test accidentali. – raindrift

10

Prova questa:

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

È necessario applicare il mock mentre i singoli test sono effettivamente in esecuzione, non solo in setUp(). Possiamo creare il simulato in setUp() e applicarlo in seguito con un gestore di contesto with ....

+0

Questo è quello che stavo chiedendo, ma la risposta di John sembra migliore per l'esempio dato. Trovo il tuo utile per altri casi però. Grazie. – Funkatic

+0

Nessun problema, felice di aiutare! – Will

0

Mi sono imbattuto in un problema in cui stavo cercando di prendere in giro le variabili utilizzate al di fuori di qualsiasi funzione o classe, il che è problematico perché vengono utilizzate nel momento in cui si tenta di prendere in giro la classe, prima di poter prendere in giro i valori.

Ho finito per utilizzare una variabile di ambiente. Se la variabile di ambiente esiste, utilizzare tale valore, altrimenti utilizzare l'impostazione predefinita dell'applicazione. In questo modo ho potuto impostare il valore della variabile di ambiente nei miei test.

Nella mia prova, ho avuto questo codice prima della classe è stato importato

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

Nella mia classe:

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

Per impostazione predefinita il mio config.LOG_PATH è /var/log/<my app name>, ma ora quando l'esecuzione del test, il percorso del log è impostato sulla directory corrente. In questo modo non è necessario l'accesso root per eseguire i test.

+1

Idealmente, i test dovrebbero essere identici su tutti gli ambienti, senza alcuna configurazione aggiuntiva. Altrimenti possono passare sul tuo computer locale ma fallire da qualche altra parte. – Funkatic