wake-up-neo.com

Verspottung einer globalen Variablen

Ich habe versucht, einige Komponententests für ein Modul zu implementieren. Ein Beispielmodul namens alphabet.py lautet wie folgt:

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

Ich möchte die Antwort aus einer Datenbank mit einigen Werten meiner Wahl verspotten, aber der folgende Code scheint nicht zu funktionieren.

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

Ich habe viele Beispiele gesehen, in denen 'patch' auf Methoden und Klassen angewendet wird, nicht jedoch auf Variablen. Ich bevorzuge es nicht, die Methode database.get zu patchen, da ich sie später möglicherweise mit anderen Parametern erneut verwenden kann. Daher würde ich eine andere Antwort benötigen.

Was mache ich hier falsch?

14
Funkatic

Sie brauchen keinen Schein zu benutzen. Importieren Sie einfach das Modul und ändern Sie den Wert des Globalen in setUp():

import alphabet

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

Versuche dies:

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

Sie müssen den Mock anwenden, während die einzelnen Tests tatsächlich ausgeführt werden, nicht nur in setUp(). Wir können erstellen den Mock in setUp() erstellen und ihn später mit einem with ... Context Manager anwenden.

21
Will

Variablen können mit (Python2) gepatcht werden 

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

Zum Beispiel:

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

Sollte auch für Python3 funktionieren, nehme ich an, da mock ein Rückport von unittest.mock

6
Valera Maniuk

Ich bin auf ein Problem gestoßen, bei dem ich versucht habe, Variablen zu simulieren, die außerhalb einer Funktion oder Klasse verwendet wurden. Dies ist problematisch, da sie in dem Moment verwendet werden, in dem Sie versuchen, die Klasse zu verspotten, bevor Sie die Werte verspotten können.

Am Ende habe ich eine Umgebungsvariable verwendet. Wenn die Umgebungsvariable vorhanden ist, verwenden Sie diesen Wert. Andernfalls verwenden Sie den Standard der Anwendung. Auf diese Weise konnte ich den Wert der Umgebungsvariablen in meinen Tests festlegen.

In meinem Test hatte ich diesen Code, bevor die Klasse importiert wurde

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

In meiner Klasse:

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

Standardmäßig ist mein config.LOG_PATH/var/log/<my app name>, aber jetzt, wenn der Test läuft, wird der Protokollpfad auf das aktuelle Verzeichnis gesetzt. Auf diese Weise benötigen Sie keinen Root-Zugriff, um die Tests auszuführen.

1
Ruth Grace Wong