2010-10-15 26 views
48

Bir çıktı dosyasını diske yazan bir Python işlevim var.Python unittest kullanarak dosya yazma fonksiyonlarının birim testi nasıl yapılır

Python unittest modülü kullanarak bunun için bir birim testi yazmak istiyorum.

Dosyaların eşitliğini nasıl doğrulamalıyım? Dosya içeriği beklenen bir + fark listesinden farklıysa bir hata almak istiyorum. Unix diff komutunun çıkışında olduğu gibi.

Bunu yapmanın herhangi bir resmi/önerilen yolu var mı?

cevap

34

En basit şey çıktı dosyasını yazmak, daha sonra içeriğini okumak, altın (beklenen) dosyasının içeriğini okumak ve bunları basit dize eşitliği ile karşılaştırmaktır. Aynı ise, çıktı dosyasını silin. Farklılarsa, bir iddia ileri sürün.

Bu şekilde, testler tamamlandığında, başarısız olan her test bir çıktı dosyasıyla temsil edilir ve bunları altın dosyalara göre ayırmak için bir üçüncü taraf aracı kullanabilirsiniz (Beyond Compare bunun için harika).

Gerçekten kendi diff çıkışınızı sağlamak istiyorsanız, Python stdlib modülünün difflib modülüne sahip olduğunu unutmayın.

def assertMultiLineEqual(self, first, second, msg=None): 
     """Assert that two multi-line strings are equal. 

     If they aren't, show a nice diff. 

     """ 
     self.assertTrue(isinstance(first, str), 
       'First argument is not a string') 
     self.assertTrue(isinstance(second, str), 
       'Second argument is not a string') 

     if first != second: 
      message = ''.join(difflib.ndiff(first.splitlines(True), 
               second.splitlines(True))) 
      if msg: 
       message += " : " + msg 
      self.fail("Multi-line strings are unequal:\n" + message) 
2

dosya işleme içerik nesli ayırmak olabilir: Python 3.1 yeni unittest destek buna benzer diffs, göstermek için kullandığı bir assertMultiLineEqual yöntemi de içerir. Böylece, geçici dosyalar ile uğraşmadan ve daha sonra temizlemeden içeriğin doğru olduğunu test edebilirsiniz.

Her bir içerik satırını oluşturan bir generator method yazarsanız, bir dosya açan ve satır sırasına göre file.writelines() numaralı telefonu arayarak bir dosya işleme yönteminiz olabilir. İki yöntem aynı sınıfta bile olabilir: test kodu jeneratörü çağırır ve üretim kodu dosya işleyicisini çağırır.

Test etmek için üç yolu gösteren bir örnek. Genellikle, sınamak için sınıfa hangi yöntemlerin kullanılabileceğine bağlı olarak bir tane seçersiniz.

import os 
from io import StringIO 
from unittest.case import TestCase 


class Foo(object): 
    def save_content(self, filename): 
     with open(filename, 'w') as f: 
      self.write_content(f) 

    def write_content(self, f): 
     f.writelines(self.generate_content()) 

    def generate_content(self): 
     for i in range(3): 
      yield u"line {}\n".format(i) 


class FooTest(TestCase): 
    def test_generate(self): 
     expected_lines = ['line 0\n', 'line 1\n', 'line 2\n'] 
     foo = Foo() 

     lines = list(foo.generate_content()) 

     self.assertEqual(expected_lines, lines) 

    def test_write(self): 
     expected_text = u"""\ 
line 0 
line 1 
line 2 
""" 
     f = StringIO() 
     foo = Foo() 

     foo.write_content(f) 

     self.assertEqual(expected_text, f.getvalue()) 

    def test_save(self): 
     expected_text = u"""\ 
line 0 
line 1 
line 2 
""" 
     foo = Foo() 

     filename = 'foo_test.txt' 
     try: 
      foo.save_content(filename) 

      with open(filename, 'rU') as f: 
       text = f.read() 
     finally: 
      os.remove(filename) 

     self.assertEqual(expected_text, text) 
+0

bunun için örnek kod verebilir misiniz? Kulağa ilginç geliyor. – buhtz

+1

Üç yaklaşımın tümü için bir @buhtz örneği ekledim. –

38

açıkça bir dosyayı kabul çıkış fonksiyonlarını olmasını tercih sap (veya dosya benzeri nesne) Bir dosya adı kabul edip dosyayı kendileri açarak yerine. Bu şekilde, bir StringIO nesnesini, birim testimde çıkış fonksiyonuna, daha sonra içeriğine (StringIO nesnesinden geri) (.seek(0) çağrısından sonra) ve beklenen çıktımla karşılaştırılabilir.

Örneğin, bu

##File:lamb.py 
import sys 


def write_lamb(outfile): 
    outfile.write("Mary had a little lamb.\n") 


if __name__ == '__main__': 
    with open(sys.argv[1], 'w') as outfile: 
     write_lamb(outfile) 



##File test_lamb.py 
import unittest 
from io import StringIO 

import lamb 


class LambTests(unittest.TestCase): 
    def test_lamb_output(self): 
     outfile = StringIO() 
     # NOTE: Alternatively, for Python 2.6+, you can use 
     # tempfile.SpooledTemporaryFile, e.g., 
     #outfile = tempfile.SpooledTemporaryFile(10 ** 9) 
     lamb.write_lamb(outfile) 
     outfile.seek(0) 
     content = outfile.read() 
     self.assertEqual(content, "Mary had a little lamb.\n") 

Bu yaklaşım gibi kodlamak için bu

##File:lamb.py 
import sys 


def write_lamb(outfile_path): 
    with open(outfile_path, 'w') as outfile: 
     outfile.write("Mary had a little lamb.\n") 


if __name__ == '__main__': 
    write_lamb(sys.argv[1]) 



##File test_lamb.py 
import unittest 
import tempfile 

import lamb 


class LambTests(unittest.TestCase): 
    def test_lamb_output(self): 
     outfile_path = tempfile.mkstemp()[1] 
     try: 
      lamb.write_lamb(outfile_path) 
      contents = open(tempfile_path).read() 
     finally: 
      # NOTE: To retain the tempfile if the test fails, remove 
      # the try-finally clauses 
      os.remove(outfile_path) 
     self.assertEqual(result, "Mary had a little lamb.\n") 

gibi kod geçiş olur mesela eğer daha esnek çıktı fonksiyonunu yapma yararı vardır, sen Bir dosyaya yazmak istemediğinize karar verebilirsiniz, ancak başka bir tampon, tüm dosya benzeri nesneleri kabul edeceğinden.

StringIO kullanarak, test çıktısının içeriğinin ana belleğe sığabileceğini varsayar.Çok büyük çıktılar için, temporary file yaklaşımını kullanabilirsiniz (ör. tempfile.SpooledTemporaryFile).

+2

Bu daha sonra diske bir dosya yazmak daha iyidir. Eğer tonlarca taklit iseniz, IO diske her türlü problemi, özellikle de onları temizlemeye çalışıyorsunuz. Diske yazma testleri yaptım, yazılı dosyaları silerken gözyaşı döktüm. Testler her seferinde iyi çalışır, ardından Tümünü Çalıştır başarısız olur. En azından bir Win makinasında Visual Studio ve PyTools ile. Ayrıca, hız. – srock

+1

Bu, ayrı işlevleri sınamak için güzel bir çözüm olsa da, programınızın sağladığı gerçek arabirimi sınarken (örneğin bir CLI aracı) sınamak yine de zahmetlidir. – Joost

+0

'seek()' ve 'read()' ile değiştirebilirsiniz GetValue() '. –

-2

Önerilere dayanarak aşağıdakileri yaptım. Ben gerçekten yeniden kullanılabilir assert yöntemini olması gerekir, böylece dosyaları okuma/yazma için gereken işlevlerin çok var gibi

class MyTestCase(unittest.TestCase): 
    def assertFilesEqual(self, first, second, msg=None): 
     first_f = open(first) 
     first_str = first_f.read() 
     second_f = open(second) 
     second_str = second_f.read() 
     first_f.close() 
     second_f.close() 

     if first_str != second_str: 
      first_lines = first_str.splitlines(True) 
      second_lines = second_str.splitlines(True) 
      delta = difflib.unified_diff(first_lines, second_lines, fromfile=first, tofile=second) 
      message = ''.join(delta) 

      if msg: 
       message += " : " + msg 

      self.fail("Multi-line strings are unequal:\n" + message) 

Bir alt sınıf MyTestCase yarattı. Şimdi testlerimde, MyTestCase'i unittest.TestCase yerine alt sınıflara ayıracağım.

Bunun hakkında ne düşünüyorsunuz?

+2

bkz http://stackoverflow.com/questions/4617034/python-open-multiple-files-using-with-open –

14
import filecmp 

Sonra

self.assertTrue(filecmp.cmp(path1, path2))