J'ai essayé
from mock import Mock
import __builtin__
__builtin__.print = Mock()
Mais cela soulève une erreur de syntaxe. J'ai aussi essayé de le patcher comme si
@patch('__builtin__.print')
def test_something_that_performs_lots_of_prints(self, mock_print):
# assert stuff
Y a-t-il un moyen de faire ça?
print
est un mot clé dans python 2.x, son utilisation en tant qu'attribut génère une syntaxe. Vous pouvez éviter cela en utilisant from __future__ import print_function
au début du fichier.
Remarque: vous ne pouvez pas simplement utiliser setattr
, car la fonction d'impression que vous avez modifiée ne sera pas appelée à moins que l'instruction print
ne soit désactivée.
Éditer: vous devez également from __future__ import print_function
dans chaque fichier dans lequel vous voulez que votre fonction print
modifiée soit utilisée, sinon il sera masqué par l'instruction print
.
Je sais qu'il existe déjà une réponse acceptée, mais qu'il existe une solution plus simple à ce problème - se moquer de l'impression dans Python 2.x. La réponse se trouve dans le didacticiel de la bibliothèque fictive: http://www.voidspace.org.uk/python/mock/patch.html et c’est:
>>> from StringIO import StringIO
>>> def foo():
... print 'Something'
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()
Bien sûr, vous pouvez aussi utiliser l'affirmation suivante:
self.assertEqual("Something\n", mock_stdout.getvalue())
J'ai vérifié cette solution dans mes unittests et cela fonctionne comme prévu. J'espère que ça aide quelqu'un. À votre santé!
Il s’agit d’une solution beaucoup plus simple pour Python 3: il est plus facile d’utiliser unittest.mock
directement dans la fonction intégrée print
, plutôt que de manipuler sys.stdout
:
from unittest.mock import patch, call
@patch('builtins.print')
def test_print(mocked_print):
print('foo')
print()
assert mocked_print.mock_calls == [call('foo'), call()]
Si vous souhaitez vous en tenir à l'instruction print
de 2.x plutôt qu'à la fonction print()
de 2.x, vous pouvez vous moquer de votre sys.stdout
à la place.
Ecrivez un "fichier" factice, peut-être de cette manière:
class Writable(object):
"""Class which has the capability to replace stdout."""
newwrite = None
def __init__(self, oldstdout, newwrite=None):
self.oldstdout = oldstdout
if newwrite is not None:
self.newwrite = newwrite
def write(self, data):
self.newwrite(self.oldstdout, data)
@classmethod
def subclass(cls, writefunc):
newcls = type('', (cls,),
dict(write=lambda self, data: writefunc(self.oldstdout, data)
return newcls
Cette classe s'attend à être combinée avec une fonction d'écriture qui récupère les données imprimées. Cette fonction d'écriture est supposée prendre 2 arguments: le premier avec "l'ancien stdout" à utiliser pour l'impression à la fin, et un autre pour les données.
Prenons
def mywrite(sink, data):
sink.write(data.encode("hex"))
pour ça.
Maintenant tu peux faire
import sys
sys.stdout = Writable(sys.stdout, mywrite)
ou tu peux faire
@Writable.subclass
def mywritable(sink, data)
sink.write(data.encode("hex"))
sys.stdout = mywritable(sys.stdout)
La 2ème version est un peu plus compliquée: elle crée une sous-classe de la Writable
à l'aide d'une fonction décoratrice qui transforme la fonction donnée en une méthode de la nouvelle classe créée à la place et placée dans le nom d'où provient cette fonction.
Après cela, vous avez une nouvelle classe qui peut être instanciée avec "old stdout" en argument et peut remplacer sys.stdout
après cela.
Ma version.
Dans le programme testé (ex: pp.py
):
from __future__ import print_function
def my_func():
print('hello')
Dans le programme de test:
def test_print(self):
from pp import my_func
from mock import call
with mock.patch('__builtin__.print') as mock_print:
my_func()
mock_print.assert_has_calls(
[
call('hello')
]
)
import mock
import sys
mock_stdout = mock.Mock()
sys.stdout = mock_stdout
print 'Hello!'
sys.stdout = sys.__stdout__
print mock_stdout.mock_calls
[call.write('Hello!'), call.write('\n')]
Cet exemple Python 3 repose sur la réponse à Python 2 de Krzysztof. Il utilise unittest.mock
. Il utilise une méthode d'assistance réutilisable pour effectuer l'assertion.
import io
import unittest
import unittest.mock
from .solution import fizzbuzz
class TestFizzBuzz(unittest.TestCase):
@unittest.mock.patch('sys.stdout', new_callable=io.StringIO)
def assert_stdout(self, n, expected_output, mock_stdout):
fizzbuzz(n)
self.assertEqual(mock_stdout.getvalue(), expected_output)
def test_only_numbers(self):
self.assert_stdout(2, '1\n2\n')
Tout d'abord, le module s'appelle __builtins__
et vous n'avez pas besoin de l'importer.
Désormais, dans Python 2, print
est un mot clé, vous ne pouvez donc pas l'utiliser directement comme nom d'attribut. Vous pouvez utiliser setattr
/getattr
pour contourner le problème:
getattr(__builtins__, "print")
Une autre option consiste à utiliser from __future__ import print_function
, qui modifie la manière dont Python analyse le module en utilisant la syntaxe Python 3.
Comme le dit lcq, print est un mot clé. Réfléchissez donc à ce que cela signifierait si vous réussissiez réellement à corriger/à vous moquer des impressions sous Python 2.7.3. Vous auriez un code comme celui-ci:
print "Hi."
se transformer en:
<MagicMock id='49489360'> "Hi."
Les objets MagicMock ne sont pas accessibles de cette façon, vous obtiendrez donc une erreur de syntaxe.
Donc voilà. Vous ne pouvez que vous moquer de l’impression Python3 function ou sys.stdout.