Meilleure façon de simuler un attribut de classe dans le test de l'unité python
j'ai une classe de base qui définit un attribut de classe et certaines classes enfant qui en dépendent, par exemple,
class Base(object):
assignment = dict(a=1, b=2, c=3)
je veux unittest cette classe avec différents affectations, p.ex. dictionnaire vide, article unique, etc. Ceci est extrêmement simplifié bien sûr, il ne s'agit pas de réaménager mes cours ou mes tests
L' (pytest) tests je suis venu avec, éventuellement, que les travaux sont
from .base import Base
def test_empty(self):
with mock.patch("base.Base.assignment") as a:
a.__get__ = mock.Mock(return_value={})
assert len(Base().assignment.values()) == 0
def test_single(self):
with mock.patch("base.Base.assignment") as a:
a.__get__ = mock.Mock(return_value={'a':1})
assert len(Base().assignment.values()) == 1
cela semble assez compliqué et hacky-I ne comprenez même pas tout à fait pourquoi cela fonctionne (je suis familier avec les descripteurs cependant). N'maquette magiquement transformer les attributs de classe dans les descripteurs?
Une solution qui permettrait de se sentir plus logique ne fonctionne pas:
def test_single(self):
with mock.patch("base.Base") as a:
a.assignment = mock.PropertyMock(return_value={'a':1})
assert len(Base().assignment.values()) == 1
ou
def test_single(self):
with mock.patch("base.Base") as a:
a.assignment = {'a':1}
assert len(Base().assignment.values()) == 1
les autres variantes que j'ai essayées ne fonctionnent pas non plus (les assignations restent inchangées dans le test).
Quelle est la bonne façon de se moquer d'un attribut de classe? Est-il mieux / plus compréhensible que l' l'un au-dessus?
5 réponses
base.Base.assignment
est simplement remplacé par un Mock
objet. Vous un descripteur par l'ajout d'un __get__
méthode.
c'est un peu verbeux et un peu inutile; vous pouvez simplement mettre base.Base.assignment
directement:
def test_empty(self):
Base.assignment = {}
assert len(Base().assignment.values()) == 0
ce n'est pas très sûr quand on utilise la simultanéité des tests, bien sûr.
Pour un PropertyMock
, j'utilise:
with patch('base.Base.assignment', new_callable=PropertyMock) as a:
a.return_value = {'a': 1}
ou encore:
with patch('base.Base.assignment', new_callable=PropertyMock,
return_value={'a': 1}):
pour améliorer la lisibilité, vous pouvez utiliser le @patch
décorateur:
from mock import patch
from unittest import TestCase
from base import Base
class MyTest(TestCase):
@patch('base.Base.assignment')
def test_empty(self, mock_assignment):
# The `mock_assignment` is a MagicMock instance,
# you can do whatever you want to it.
mock_assignment.__get__.return_value = {}
self.assertEqual(len(Base().assignment.values()), 0)
# ... and so on
Vous pouvez trouver plus de détails à http://www.voidspace.org.uk/python/mock/patch.html#mock.patch.
si votre classe (File par exemple) dans déjà importé à l'intérieur de votre test et vous souhaitez patch MAX_RESTRY attr - vous pouvez utiliser @patch.l'objet ou tout simplement mieux @patch.plusieurs
from mock import patch, PropertyMock, Mock
from somewhere import Queue
@patch.multiple(Queue, MAX_RETRY=1, some_class_method=Mock)
def test_something(self):
do_something()
@patch.object(Queue, 'MAX_RETRY', return_value=1, new_callable=PropertyMock)
def test_something(self, _mocked):
do_something()
peut-être que je manque quelque chose, mais n'est-ce pas possible sans utiliser PropertyMock
?
with mock.patch.object(Base, 'assignment', {'bucket': 'head'}):
# do stuff
voici un exemple pour tester votre Base
catégorie:
- moqueur attributs de classes multiples de différents types (c'est-à-dire:
dict
etint
) @patch
décorateur etpytest
cadrepython 2.7+
ou3+
.
# -*- coding: utf-8 -*-
try: #python 3
from unittest.mock import patch, PropertyMock
except ImportError as e: #python 2
from mock import patch, PropertyMock
from base import Base
@patch('base.Base.assign_dict', new_callable=PropertyMock, return_value=dict(a=1, b=2, c=3))
@patch('base.Base.assign_int', new_callable=PropertyMock, return_value=9765)
def test_type(mock_dict, mock_int):
"""Test if mocked class attributes have correct types"""
assert isinstance(Base().assign_dict, dict)
assert isinstance(Base().assign_int , int)