2013-06-13 16 views
7

parser = ParserClass('/path/to/file') gibi bir dosya adı vererek örnek oluşturduğum bir sınıf var, sonra dosyayı açan ve okuyan parser.parse() yöntemini arıyorum.
Şimdi kötü bir şey içeride oluyor eğer o birim testi istiyorum:Python'da alaylı bir dosya nesnesiyle birim testi nasıl yapılır?

with open(filename, 'rb') as fp: 
    // do something 

doğru İstisna yükseltilecek, bu yüzden böyle __builtin__.open alay etmek istiyorum:

from mock import MagicMock, patch 
from StringIO import StringIO 

test_lines = StringIO("""some test lines, emulating a real file content""") 
mock_open = MagicMock(return_value=test_lines) 
with patch('__builtin__.open', mock_open): 
    self.mock.parse() 

ama bu bana verir bir AttributeError: StringIO instance has no attribute '__exit__'.
StringIO, aynen bir dosya nesnesi gibi davranıyor, ama öyle görünüyor, öyle değil.

Bu yöntemi sahte nesnelerle belirli bir içerikle (test_lines) nasıl test edebilirim? Bunun yerine ne kullanmalıyım?

cevap

9

Bir bağlam yöneticisi sağlamak için StringIO alt sınıf olabilir:

class ContextualStringIO(StringIO): 
    def __enter__(self): 
     return self 
    def __exit__(self, *args): 
     self.close() # icecrime does it, so I guess I should, too 
     return False # Indicate that we haven't handled the exception, if received 


test_lines = ContextualStringIO(...) 

Brüt spekülasyon: StringIO nesneler açılan değiştirmeler file nesneler için dışında bir bağlam yöneticisi olmaması için ise, ben Bunun işe yarayacağını merak et:

class ContextualStringIO(StringIO, file): 
    pass 

ContextualStringIO neyi devralıyor le işlemleri StringIO'dan itibaren yapabilir, ancak diğer her şey file'dan devralınır. Zarif görünüyor, ancak büyük olasılıkla kapsamlı bir test (ya da Python internals ile bu işe yaramayacağını açıklamak için daha fazla tanıdık biri) gerektirir.

+0

'TypeError: __exit __() tam olarak 1 argüman alır (4 verilen)' – kissgyorgy

+0

@Walkman: Teşekkürler. __exit__' tarafından alınabilecek istisnalardan herhangi birini ele almayacağım için, keyfi bir sayıda argüman almak için güncelledim. Uygun bir uygulama; Herhangi bir özel durumun üstesinden gelip gelmediğini öğrenmek için "open" tarafından sağlanan içerik yöneticisine yeterince aşina değilim veya yalnızca dosyayı kapatır ve herhangi bir özel durumun yeniden yükseltilmesine izin verir. – chepner

+1

[Dokümantasyon:] (http://docs.python.org/2/reference/compound_stmts.html#with) Paket bir istisna nedeniyle çıkıldıysa ve __exit __() yönteminden döndürülen değer yanlış, istisna yeniden. Dönüş değeri true ise, istisna bastırılır ve yürütme, ifadeyi içeren ifadeyle devam eder. – kissgyorgy

6

Bu, StringIO'un içerik yöneticisi protokolünü uygulamadığı bilinen bir sorundur.

from contextlib import contextmanager 


@contextmanager 
def StringIO(): 
    """Add support for 'with' statement to StringIO - http://bugs.python.org/issue1286 
    """ 
    try: 
     from cStringIO import StringIO 
    except ImportError: 
     from StringIO import StringIO 

    sio = StringIO() 

    try: 
     yield sio 
    finally: 
     sio.close() 

O StringIO için bağlam yöneticisi protokolünü uygulayan ve bir with açıklamada kullanılmasını sağlar:

A common recipe şudur.


GÜNCELLEME Eh, ben sadece doğrudan bir dize gelen okuyabilir mock_open varlığını keşfetti, bu yüzden muhtemelen gitmek için bir yoldur. (Not söylediği gibi) bu sadece unicode tipiyle kullanılabilir Ancak

The io module provides the Python interfaces to stream handling. Under Python 2.x, this is proposed as an alternative to the built-in file object, but in Python 3.x it is the default interface to access files and streams.

:

+0

Teşekkürler! Bu çalışır, ancak bir parametre ile yapılandırabilmek için 'def StringIO (data)' ve 'sio = StringIO (data)' eklemelisiniz. – kissgyorgy

+0

Sadece yeniden vurgulamak için, güncelleme önemlidir, 'mock_open' bu amaç için ve http://bugs.python.org/issue21258 olmasına rağmen ideal çözüm olmalıdır. – 0xc0de

0

özellikle mock kütüphanede bu amaçla, a provision vardır:

O (. Eğer hemen for line in opened_mock_file yapamaz bölümü yani __iter__ yöntemi) varsayılan yineleyici desteği eksik bir issue var, ama yok here tarif edildiği gibi çalışılabilir.

@ icecrime'ın yanıtını yazdım, ancak güncelleme bölümünün yeterince vurgulanmış gibi görünmediğini hissediyorum.