web-dev-qa-db-fra.com

Fonction d'impression intégrée de Mock Python

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? 

35
aychedee

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.

15
quantum

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é!

44
Krzysztof Czeronko

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()]
5
Tom

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.

3
glglgl

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')
            ]
        )
3
Chien-Wei Huang
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')]
1
sgjurano

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')
0
A-B-B

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.

0
lqc

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.

0
dbn