2010-10-15 13 views
48

का उपयोग कर फ़ाइलों को लिखने के कार्यों के यूनिट परीक्षण कैसे करें मेरे पास एक पाइथन फ़ंक्शन है जो डिस्क पर आउटपुट फ़ाइल लिखता है।पाइथन unittest

मैं पाइथन unittest मॉड्यूल का उपयोग कर इसके लिए एक इकाई परीक्षण लिखना चाहता हूँ।

मुझे फ़ाइलों की समानता कैसे दी जानी चाहिए? यदि फ़ाइल सामग्री अपेक्षित एक + अंतर की सूची से भिन्न होती है तो मैं एक त्रुटि प्राप्त करना चाहता हूं। यूनिक्स diff कमांड के आउटपुट के रूप में।

क्या ऐसा करने का कोई आधिकारिक/अनुशंसित तरीका है?

उत्तर

34

आउटपुट फ़ाइल लिखना सबसे आसान बात है, फिर इसकी सामग्री पढ़ें, सोना (अपेक्षित) फ़ाइल की सामग्री पढ़ें, और सरल स्ट्रिंग समानता के साथ उनकी तुलना करें। अगर वे वही हैं, तो आउटपुट फ़ाइल हटाएं। यदि वे अलग हैं, तो एक दावा उठाएं।

इस तरह, जब परीक्षण किए जाते हैं, तो प्रत्येक असफल परीक्षण को आउटपुट फ़ाइल के साथ प्रदर्शित किया जाएगा, और आप सोने की फाइलों के खिलाफ उन्हें अलग करने के लिए एक तृतीय पक्ष टूल का उपयोग कर सकते हैं (तुलना के अलावा तुलना इसके लिए अद्भुत है)।

यदि आप वास्तव में अपना स्वयं का diff आउटपुट प्रदान करना चाहते हैं, तो याद रखें कि पायथन stdlib में difflib मॉड्यूल है।

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

आप फ़ाइल से निपटने से सामग्री पीढ़ी को अलग कर सकता: अजगर 3.1 में नया unittest समर्थन एक assertMultiLineEqual विधि यह डिफ, इस के समान दिखाने के लिए उपयोग भी शामिल है। इस तरह, आप परीक्षण कर सकते हैं कि सामग्री अस्थायी फ़ाइलों के साथ गड़बड़ किए बिना और बाद में उन्हें साफ करने के बिना सही है।

यदि आप generator method लिखते हैं जो सामग्री की प्रत्येक पंक्ति उत्पन्न करता है, तो आपके पास एक फ़ाइल हैंडलिंग विधि हो सकती है जो फ़ाइल खोलती है और लाइनों के अनुक्रम के साथ file.writelines() पर कॉल करती है। दो विधियां एक ही कक्षा में भी हो सकती हैं: टेस्ट कोड जनरेटर को कॉल करेगा, और उत्पादन कोड फ़ाइल हैंडलर को कॉल करेगा।

यहां एक उदाहरण है जो परीक्षण के सभी तीन तरीकों को दिखाता है। आमतौर पर, परीक्षण के लिए कक्षा में कौन सी विधियां उपलब्ध हैं, इस पर निर्भर करते हुए आप केवल एक चुनते हैं।

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

आपको लगता है कि के लिए उदाहरण कोड प्रदान कर सकता है? यह दिलचस्प लगता है। – buhtz

+1

मैंने सभी तीन दृष्टिकोणों के लिए एक उदाहरण जोड़ा, @buhtz। –

38

मैं स्पष्ट रूप से एक फ़ाइल को स्वीकार उत्पादन कार्यों को पसंद संभाल (या फ़ाइल की तरह वस्तु), बजाय एक फ़ाइल नाम स्वीकार करते हैं और फ़ाइल के लिए खुद को खोलने। इस तरह, मैं StringIO ऑब्जेक्ट को अपने यूनिट टेस्ट में आउटपुट फ़ंक्शन पर पास कर सकता हूं, फिर .read() उस StringIO ऑब्जेक्ट से पहले की सामग्री (.seek(0) कॉल के बाद) और मेरे अपेक्षित आउटपुट की तुलना करें।

उदाहरण के लिए, हम कोड इस

##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") 

की तरह इस

##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") 

यह दृष्टिकोण की तरह कोड के लिए संक्रमण होगा उदाहरण के लिए और अधिक लचीला है, तो अपने उत्पादन समारोह बनाने, के अतिरिक्त लाभ है, तो आप तय करें कि आप किसी फ़ाइल को लिखना नहीं चाहते हैं, लेकिन कुछ अन्य बफर, क्योंकि यह सभी फाइल-जैसी ऑब्जेक्ट्स स्वीकार करेगा।

ध्यान दें कि StringIO का उपयोग करके परीक्षण आउटपुट की सामग्री मुख्य स्मृति में फिट हो सकती है।बहुत बड़े आउटपुट के लिए, आप temporary file दृष्टिकोण (उदा।, tempfile.SpooledTemporaryFile) का उपयोग कर सकते हैं।

+2

डिस्क पर फ़ाइल लिखना बेहतर है। यदि आप कई unittests चला रहे हैं, डिस्क पर आईओ सभी प्रकार की समस्याओं का कारण बनता है, विशेष रूप से उन्हें साफ करने की कोशिश कर रहा है। मैंने डिस्क पर लिखने का परीक्षण किया था, लिखित फाइलों को हटाने वाले आंसू। टेस्ट एक समय में ठीक काम करेंगे, फिर सभी को चलाने में असफल हो जाते हैं। विन मशीन पर कम से कम विजुअल स्टूडियो और पायटूल के साथ। इसके अलावा, गति। – srock

+1

हालांकि यह अलग-अलग कार्यों का परीक्षण करने का एक अच्छा समाधान है, लेकिन आपके प्रोग्राम द्वारा प्रदान किए जाने वाले वास्तविक इंटरफ़ेस का परीक्षण करते समय भी यह परेशानी होती है (उदाहरण के लिए एक सीएलआई उपकरण) .. – Joost

+0

आप 'seek()' और 'read()' ' getValue() '। –

-2

सुझावों के आधार पर मैंने निम्नलिखित किया।

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) 

मैं एक उपवर्ग MyTestCase बनाया के रूप में मैं काम करता है पढ़ने के लिए/फ़ाइलें लिखने की जरूरत है कि इतने मैं वास्तव में फिर से प्रयोग करने योग्य ज़ोर विधि की आवश्यकता के बहुत सारे है। अब मेरे परीक्षणों में, मैं unittest.TestCase के बजाय MyTestCase subclass होगा।

आप इसके बारे में क्या सोचते हैं?

+2

देखें http://stackoverflow.com/questions/4617034/python-open-multiple-files-using-with-open –

14
import filecmp 

फिर

self.assertTrue(filecmp.cmp(path1, path2)) 
संबंधित मुद्दे