Función de impresión incorporada de Mock Python

He intentado

from mock import Mock import __builtin__ __builtin__.print = Mock() 

Pero eso plantea un error de syntax. También he intentado parchearlo así

 @patch('__builtin__.print') def test_something_that_performs_lots_of_prints(self, mock_print): # assert stuff 

¿Hay alguna manera de hacer esto?

print es una palabra clave en python 2.x, usarla como atributo genera un SyntaxError. Puede evitarlo utilizando from __future__ import print_function al principio del archivo.

Nota: no puede simplemente usar setattr , porque la función de impresión que modificó no se invoca a menos que la statement de print esté deshabilitada.

Edición: también debe from __future__ import print_function de from __future__ import print_function en cada archivo que desee que se use, o la statement de print la enmascarará.

Sé que ya hay una respuesta aceptada, pero hay una solución más sencilla para ese problema: burlarse de la impresión en Python 2.x. La respuesta está en el tutorial de la biblioteca simulada: http://www.voidspace.org.uk/python/mock/patch.html y es:

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

Por supuesto que puede usar también la siguiente afirmación:

 self.assertEqual("Something\n", mock_stdout.getvalue()) 

He comprobado esta solución en mis pruebas de unidad y está funcionando como se esperaba. Espero que esto ayude a alguien. ¡Aclamaciones!

Esta es una solución mucho más sencilla de Python 3: es más fácil usar unittest.mock directamente en la función de print incorporada, en lugar de sys.stdout con 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 desea seguir con la statement de print de 2.x en lugar de la función print() de 2.x, podría burlarse de su sys.stdout en su lugar.

Escribe un “archivo” ficticio, tal vez de esta manera:

 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 

Esta clase espera combinarse con una función de escritura que obtiene los datos impresos. Se supone que esta función de escritura debe tomar 2 argumentos: el primero con el “stdout antiguo” que se usará para imprimir al final, y otro más para los datos.

Echemos

 def mywrite(sink, data): sink.write(data.encode("hex")) 

para eso.

Ahora puedes hacer

 import sys sys.stdout = Writable(sys.stdout, mywrite) 

o puedes hacer

 @Writable.subclass def mywritable(sink, data) sink.write(data.encode("hex")) sys.stdout = mywritable(sys.stdout) 

La segunda versión es un poco más complicada: crea una subclase de la Writable con la ayuda de una función decoradora que convierte la función dada en un método de la nueva clase creada y se pone en el nombre de donde proviene la función dada.

Después de eso, tiene una nueva clase que puede ser instanciada con el “antiguo stdout” como argumento y puede reemplazar sys.stdout después de eso.

Mi version.

En el progtwig probado (ej: pp.py ):

 from __future__ import print_function def my_func(): print('hello') 

En el progtwig de prueba:

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

Primero, el módulo se llama __builtins__ y no es necesario que lo importe.

Ahora, en Python 2, print es una palabra clave, por lo que no puede usarla directamente como nombre de atributo. Puede usar setattr / getattr para solucionarlo:

 getattr(__builtins__, "print") 

Otra opción es utilizar from __future__ import print_function que cambia la forma en que Python analiza el módulo a la syntax de Python 3.

Como dice lcq, imprimir es una palabra clave. Entonces, piense en lo que significaría si realmente tuviera éxito en la impresión de parches / simulacros en Python 2.7.3. Tendrías un código como este:

 print "Hi." 

conviertiéndose en:

  "Hi." 

No se puede acceder a los objetos de MagicMock de esta manera, por lo que obtendría un error de syntax.

Así que sí. Solo puede burlarse de la función de impresión Python3 o sys.stdout.

Este ejemplo de Python 3 se basa en la respuesta de Python 2 de Krzysztof. Utiliza unittest.mock . Utiliza un método de ayuda reutilizable para realizar la aserción.

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