2017-02-16 1 views
3

J'essaie de comprendre les différentes façons de patcher une constante en Python en utilisant mock.patch. Mon but est de pouvoir utiliser une variable définie dans ma classe Test comme valeur de patch pour ma constante.Comment patcher une constante en Python en utilisant un simulacre comme paramètre de fonction

J'ai trouvé cette question qui explique comment patcher une constante: How to patch a constant in python Et cette question qui explique comment utiliser l'auto dans le patch: using self in python @patch decorator

Mais à partir de ce 2ème lien, je ne peux pas obtenir le testtwo façon (fournir la maquette en tant que paramètre de fonction) pour travailler

Voici mon utilisation simplifiée cas:

ma module.py

MY_CONSTANT = 5 

def get_constant(): 
    return MY_CONSTANT 

test_mymodule.py

import unittest 
from unittest.mock import patch 

import mymodule 

class Test(unittest.TestCase): 

    #This works 
    @patch("mymodule.MY_CONSTANT", 3) 
    def test_get_constant_1(self): 
     self.assertEqual(mymodule.get_constant(), 3) 

    #This also works 
    def test_get_constant_2(self): 
     with patch("mymodule.MY_CONSTANT", 3): 
      self.assertEqual(mymodule.get_constant(), 3) 

    #But this doesn't 
    @patch("mymodule.MY_CONSTANT") 
    def test_get_constant_3(self, mock_MY_CONSTANT): 
     mock_MY_CONSTANT.return_value = 3 
     self.assertEqual(mymodule.get_constant(), 3) 
     #AssertionError: <MagicMock name='MY_CONSTANT' id='64980808'> != 3 

Je pense que je shoudln't utilise return_value, parce que mock_MY_CONSTANT est pas une fonction. Alors, quel attribut suis-je censé utiliser pour remplacer la valeur renvoyée lorsque la constante est appelée?

Répondre

-1

Vous pouvez simpy attribuer une valeur fausse à la constante avant chaque assert:

def test_get_constant_3(self): 
    mymodule.MY_CONSTANT = 3 
    self.assertEqual(mymodule.get_constant(), 3) 
    mymodule.MY_CONSTANT = 7 
    self.assertEqual(mymodule.get_constant(), 7) 

Certains autre exemple

# --- config.py --- 

class AppConf: 
    APP_TIMEZONE = os.environ.get['APP_TIMEZONE'] 



# --- my_mod.py --- 

from datetime import datetime 
from config import AppConf 

LOCAL_TZ = AppConf.APP_TIMEZONE 

def to_local_tz(dt_obj, tz): 
    """Return datetime obj for specific timezone""" 
    # some code here 
    return local_dt_obj 

def get_local_time(): 
    return to_local_tz(datetime.utcnow(), LOCAL_TZ).strftime('%H:%M') 



# --- test_my_mod.py --- 

import my_mod 

class TestMyMod(unittest.TestCase): 
    @patch('my_mod.datetime') 
    def test_get_local_time(self, mock_dt): 
     # Mock to 15:00 UTC 
     mock_dt.utcnow.return_value = datetime(2017, 5, 3, 15) 

     # Test with TZ 'Europe/Kiev'  +02:00 +03:00(DST) 
     my_mod.LOCAL_TZ = 'Europe/Kiev' 
     assert my_mod.get_local_time() == '18:00' 

     # Test with TZ 'America/New_York' -05:00 -04:00(DST) 
     my_mod.LOCAL_TZ = 'America/New_York' 
     assert my_mod.get_local_time() == '11:00' 

donc pas besoin de patcher une constante du tout

+0

D'abord merci de me répondre! Oui mais dans ce cas, cela signifie que la valeur de mymodule.MY_CONSTANT est modifiée pour tous les tests à venir. C'est pourquoi je veux utiliser patch à la place, donc il est limité à la portée où je corrige. –

+0

Ensuite, utilisez 'avec patch():' pour votre cas. De quoi avez-vous besoin? Je n'arrive pas à comprendre le problème, désolé. –

+0

Dans la variante # 3 vous obtenez le même résultat que # 2 de toute façon, pourquoi vous voulez quelque chose comme le format # 3? –

0

Pour répondre à la question théoriquement, je pense que vous auriez besoin de patcher la méthode __int__() sur la constante, comme ça:

@patch("mymodule.MY_CONSTANT.__int__") 
def test_get_constant_4(self, mock_MY_CONSTANT): 
    mock_MY_CONSTANT.return_value = 3 
    self.assertEqual(mymodule.get_constant(), 3) 

Évidemment, c'est la mauvaise façon de procéder, comme le montre l'erreur qu'il déclenche.

AttributeError: 'int' object attribute '__int__' is read-only