Python Language
इकाई का परीक्षण
खोज…
टिप्पणियों
पायथन के लिए कई यूनिट परीक्षण उपकरण हैं। यह दस्तावेज़ीकरण विषय बेसिक unittest
मॉड्यूल का वर्णन करता है। अन्य परीक्षण उपकरण शामिल हैं py.test
और nosetests
। परीक्षण के बारे में यह अजगर प्रलेखन गहराई में जाने के बिना इनमें से कई उपकरणों की तुलना करता है।
परीक्षण के अपवाद
उदाहरण के लिए जब गलत इनपुट दिया जाता है तो प्रोग्राम त्रुटियां फेंक देते हैं। इस वजह से, किसी को यह सुनिश्चित करने की आवश्यकता होती है कि वास्तविक गलत इनपुट दिए जाने पर कोई त्रुटि होती है। उसके कारण हमें एक सटीक अपवाद की जांच करने की आवश्यकता है, इस उदाहरण के लिए हम निम्नलिखित अपवाद का उपयोग करेंगे:
class WrongInputException(Exception):
pass
यह अपवाद तब उठाया जाता है जब गलत इनपुट दिया जाता है, निम्नलिखित संदर्भ में जहां हम हमेशा टेक्स्ट इनपुट के रूप में एक संख्या की उम्मीद करते हैं।
def convert2number(random_input):
try:
my_input = int(random_input)
except ValueError:
raise WrongInputException("Expected an integer!")
return my_input
यह जाँचने के लिए कि क्या कोई अपवाद उठाया गया है, हम उस अपवाद की जाँच करने के लिए assertRaises
का उपयोग करते हैं। assertRaises
उपयोग दो तरीकों से किया जा सकता है:
- नियमित फ़ंक्शन कॉल का उपयोग करना। पहला तर्क अपवाद प्रकार लेता है, दूसरा एक कॉल करने योग्य (आमतौर पर एक फ़ंक्शन) और बाकी तर्क इस कॉल करने योग्य के लिए पारित किए जाते हैं।
- क्लॉज के
with
उपयोग करना, फ़ंक्शन को केवल अपवाद प्रकार देता है। इसका यह फायदा है कि अधिक कोड को निष्पादित किया जा सकता है, लेकिन देखभाल के साथ इसका उपयोग किया जाना चाहिए क्योंकि कई फ़ंक्शन समान अपवाद का उपयोग कर सकते हैं जो समस्याग्रस्त हो सकते हैं। एक उदाहरण: self.assertRaises (WrongInputException) के साथ: Convert2number ("संख्या नहीं")
यह पहली बार निम्नलिखित परीक्षण मामले में लागू किया गया है:
import unittest
class ExceptionTestCase(unittest.TestCase):
def test_wrong_input_string(self):
self.assertRaises(WrongInputException, convert2number, "not a number")
def test_correct_input(self):
try:
result = convert2number("56")
self.assertIsInstance(result, int)
except WrongInputException:
self.fail()
एक अपवाद की जांच करने की भी आवश्यकता हो सकती है जिसे फेंका नहीं जाना चाहिए था। हालांकि, एक परीक्षण स्वचालित रूप से विफल हो जाएगा जब एक अपवाद फेंक दिया जाता है और इस तरह बिल्कुल भी आवश्यक नहीं हो सकता है। बस विकल्पों को दिखाने के लिए, दूसरी परीक्षण विधि एक मामले को दिखाती है कि कोई अपवाद कैसे नहीं फेंक सकता है। मूल रूप से, यह अपवाद को पकड़ रहा है और फिर असफल विधि का उपयोग करके परीक्षण को fail
है।
मॉकिंग कार्य unittest.mock.create_autospec के साथ
फ़ंक्शन को मॉक करने का एक तरीका create_autospec
फ़ंक्शन का उपयोग करना है, जो किसी ऑब्जेक्ट को उसके चश्मे के अनुसार मॉक आउट करेगा। फ़ंक्शन के साथ, हम यह सुनिश्चित करने के लिए उपयोग कर सकते हैं कि उन्हें उचित रूप से कहा जाता है।
एक फ़ंक्शन के साथ custom_math.py
में multiply
:
def multiply(a, b):
return a * b
और एक समारोह multiples_of
में process_math.py
:
from custom_math import multiply
def multiples_of(integer, *args, num_multiples=0, **kwargs):
"""
:rtype: list
"""
multiples = []
for x in range(1, num_multiples + 1):
"""
Passing in args and kwargs here will only raise TypeError if values were
passed to multiples_of function, otherwise they are ignored. This way we can
test that multiples_of is used correctly. This is here for an illustration
of how create_autospec works. Not recommended for production code.
"""
multiple = multiply(integer,x, *args, **kwargs)
multiples.append(multiple)
return multiples
हम परीक्षण कर सकते हैं multiples_of
बाहर मजाक से अकेले multiply
। नीचे दिए गए उदाहरण में पायथन मानक पुस्तकालय का उपयोग एकतरफा किया गया है, लेकिन इसका उपयोग अन्य परीक्षण ढाँचों के साथ भी किया जा सकता है, जैसे कि पाइस्टेस्ट या नाक:
from unittest.mock import create_autospec
import unittest
# we import the entire module so we can mock out multiply
import custom_math
custom_math.multiply = create_autospec(custom_math.multiply)
from process_math import multiples_of
class TestCustomMath(unittest.TestCase):
def test_multiples_of(self):
multiples = multiples_of(3, num_multiples=1)
custom_math.multiply.assert_called_with(3, 1)
def test_multiples_of_with_bad_inputs(self):
with self.assertRaises(TypeError) as e:
multiples_of(1, "extra arg", num_multiples=1) # this should raise a TypeError
एक unittest.TestCase के भीतर टेस्ट सेटअप और टियरडाउन
कभी-कभी हम प्रत्येक परीक्षण के तहत चलाने के लिए एक संदर्भ तैयार करना चाहते हैं। setUp
विधि को कक्षा में प्रत्येक परीक्षण से पहले चलाया जाता है। हर टेस्ट के अंत में tearDown
चलाया जाता है। ये तरीके वैकल्पिक हैं। याद रखें कि सहकारी समितियों में अक्सर TestCases का उपयोग किया जाता है, इसलिए आपको इन तरीकों में हमेशा super
कॉल करने के लिए सावधान रहना चाहिए ताकि बेस क्लास के setUp
और tearDown
तरीकों को भी कहा जाए। के आधार कार्यान्वयन TestCase
खाली प्रदान करता है setUp
और tearDown
तरीकों इतना है कि वे अपवाद को ऊपर उठाने के बिना कहा जा सकता है:
import unittest
class SomeTest(unittest.TestCase):
def setUp(self):
super(SomeTest, self).setUp()
self.mock_data = [1,2,3,4,5]
def test(self):
self.assertEqual(len(self.mock_data), 5)
def tearDown(self):
super(SomeTest, self).tearDown()
self.mock_data = []
if __name__ == '__main__':
unittest.main()
ध्यान दें कि python2.7 + में, addCleanup
पद्धति भी है जो परीक्षण चलाने के बाद कॉल करने के लिए रजिस्टर करने का कार्य करती है। के विपरीत tearDown
जो केवल तभी कहा जाता हो जाता है setUp
सफल होता है, के माध्यम से पंजीकृत कार्यों addCleanup
भी में एक बिना क्रिया का अपवाद की स्थिति में बुलाया जाएगा setUp
। एक ठोस उदाहरण के रूप में, इस पद्धति को अक्सर विभिन्न मॉक को हटाते हुए देखा जा सकता है जो परीक्षण के दौरान पंजीकृत थे:
import unittest
import some_module
class SomeOtherTest(unittest.TestCase):
def setUp(self):
super(SomeOtherTest, self).setUp()
# Replace `some_module.method` with a `mock.Mock`
my_patch = mock.patch.object(some_module, 'method')
my_patch.start()
# When the test finishes running, put the original method back.
self.addCleanup(my_patch.stop)
इस तरह से क्लीनअप को पंजीकृत करने का एक और लाभ यह है कि यह प्रोग्रामर को सेटअप कोड के बगल में सफाई कोड डालने की अनुमति देता है और यह आपको इस घटना में बचाता है कि एक उपवर्ग tearDown
में super
कॉल करना भूल जाता है।
अपवाद पर जोर देना
आप परीक्षण कर सकते हैं कि एक फ़ंक्शन दो अलग-अलग तरीकों के माध्यम से बिल्ट-इन यूनिस्ट के साथ एक अपवाद फेंकता है।
एक संदर्भ प्रबंधक का उपयोग करना
def division_function(dividend, divisor):
return dividend / divisor
class MyTestCase(unittest.TestCase):
def test_using_context_manager(self):
with self.assertRaises(ZeroDivisionError):
x = division_function(1, 0)
यह संदर्भ प्रबंधक के अंदर कोड चलाएगा और, यदि यह सफल होता है, तो यह परीक्षण में विफल हो जाएगा क्योंकि अपवाद नहीं उठाया गया था। यदि कोड सही प्रकार का अपवाद उठाता है, तो परीक्षण जारी रहेगा।
यदि आप अतिरिक्त अपवाद की सामग्री प्राप्त करना चाहते हैं, तो आप इसके विरुद्ध अतिरिक्त दावे भी कर सकते हैं।
class MyTestCase(unittest.TestCase):
def test_using_context_manager(self):
with self.assertRaises(ZeroDivisionError) as ex:
x = division_function(1, 0)
self.assertEqual(ex.message, 'integer division or modulo by zero')
कॉल करने योग्य फ़ंक्शन प्रदान करके
def division_function(dividend, divisor):
"""
Dividing two numbers.
:type dividend: int
:type divisor: int
:raises: ZeroDivisionError if divisor is zero (0).
:rtype: int
"""
return dividend / divisor
class MyTestCase(unittest.TestCase):
def test_passing_function(self):
self.assertRaises(ZeroDivisionError, division_function, 1, 0)
पहले पैरामीटर के लिए जाँच करने के लिए अपवाद होना चाहिए, और कॉल करने योग्य फ़ंक्शन को दूसरे पैरामीटर के रूप में पारित किया जाना चाहिए। निर्दिष्ट किए गए किसी भी अन्य पैरामीटर को सीधे उस फ़ंक्शन को पास किया जाएगा, जिसे आप अपवाद को ट्रिगर करने वाले मापदंडों को निर्दिष्ट करने की अनुमति देते हैं।
Unittests के भीतर जोर चुनना
जबकि पायथन का एक assert
कथन है , पायथन इकाई परीक्षण ढांचे में परीक्षणों के लिए बेहतर दावे हैं: वे विफलताओं पर अधिक जानकारीपूर्ण हैं, और निष्पादन के डिबग मोड पर निर्भर नहीं हैं।
शायद सबसे सरल दावा है assertTrue
, जो इस तरह इस्तेमाल किया जा सकता:
import unittest
class SimplisticTest(unittest.TestCase):
def test_basic(self):
self.assertTrue(1 + 1 == 2)
यह ठीक चलेगा, लेकिन रेखा को ऊपर से बदल देगा
self.assertTrue(1 + 1 == 3)
असफल हो जायेगी।
assertTrue
अभिकथन की संभावना सबसे सामान्य अभिकथन है, क्योंकि कुछ भी परीक्षण किया जा सकता है कुछ बूलियन स्थिति के रूप में, लेकिन अक्सर बेहतर विकल्प होते हैं। जब समानता के लिए परीक्षण, ऊपर के रूप में, यह लिखना बेहतर है
self.assertEqual(1 + 1, 3)
जब पूर्व विफल रहता है, तो संदेश है
======================================================================
FAIL: test (__main__.TruthTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "stuff.py", line 6, in test
self.assertTrue(1 + 1 == 3)
AssertionError: False is not true
लेकिन जब उत्तरार्द्ध विफल हो जाता है, तो संदेश है
======================================================================
FAIL: test (__main__.TruthTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "stuff.py", line 6, in test
self.assertEqual(1 + 1, 3)
AssertionError: 2 != 3
जो अधिक जानकारीपूर्ण है (यह वास्तव में बाएं हाथ के परिणाम का मूल्यांकन करता है)।
आप मानक प्रलेखन में मुखरता की सूची पा सकते हैं। सामान्य तौर पर, यह एक अच्छा विचार है कि यह विशेष रूप से स्थिति को फिट करने वाले दावे को चुनना है। इस प्रकार, जैसा कि ऊपर दिखाया गया है, कि 1 + 1 == 2
को assertEqual
करने के लिए assertEqual
तुलना में assertTrue
का उपयोग करना बेहतर है। इसी तरह, उसने दृढ़तापूर्वक कहा कि के लिए a is None
, यह बेहतर उपयोग करने के लिए है assertIsNone
से assertEqual
।
यह भी ध्यान दें कि अभिकथन के नकारात्मक रूप हैं। इस प्रकार assertEqual
पास अपने नकारात्मक समकक्ष हैं assertNotEqual
, और assertIsNone
के अपने नकारात्मक समकक्ष हैं assertIsNotNone
। एक बार फिर, जब उचित हो तो नकारात्मक समकक्षों का उपयोग करके, त्रुटि संदेशों को स्पष्ट किया जाएगा।
यूनिट टेस्ट पाइस्टेस्ट के साथ
pytest स्थापित करना:
pip install pytest
परीक्षण तैयार हो रहे हैं:
mkdir tests
touch tests/test_docker.py
docker_something/helpers.py
में परीक्षण करने के लिए कार्य:
from subprocess import Popen, PIPE
# this Popen is monkeypatched with the fixture `all_popens`
def copy_file_to_docker(src, dest):
try:
result = Popen(['docker','cp', src, 'something_cont:{}'.format(dest)], stdout=PIPE, stderr=PIPE)
err = result.stderr.read()
if err:
raise Exception(err)
except Exception as e:
print(e)
return result
def docker_exec_something(something_file_string):
fl = Popen(["docker", "exec", "-i", "something_cont", "something"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
fl.stdin.write(something_file_string)
fl.stdin.close()
err = fl.stderr.read()
fl.stderr.close()
if err:
print(err)
exit()
result = fl.stdout.read()
print(result)
परीक्षण का आयात test_docker.py
:
import os
from tempfile import NamedTemporaryFile
import pytest
from subprocess import Popen, PIPE
from docker_something import helpers
copy_file_to_docker = helpers.copy_file_to_docker
docker_exec_something = helpers.docker_exec_something
test_docker.py
में ऑब्जेक्ट जैसी फ़ाइल को test_docker.py
:
class MockBytes():
'''Used to collect bytes
'''
all_read = []
all_write = []
all_close = []
def read(self, *args, **kwargs):
# print('read', args, kwargs, dir(self))
self.all_read.append((self, args, kwargs))
def write(self, *args, **kwargs):
# print('wrote', args, kwargs)
self.all_write.append((self, args, kwargs))
def close(self, *args, **kwargs):
# print('closed', self, args, kwargs)
self.all_close.append((self, args, kwargs))
def get_all_mock_bytes(self):
return self.all_read, self.all_write, self.all_close
Test_docker.py में test_docker.py
साथ बंदर पैचिंग:
@pytest.fixture
def all_popens(monkeypatch):
'''This fixture overrides / mocks the builtin Popen
and replaces stdin, stdout, stderr with a MockBytes object
note: monkeypatch is magically imported
'''
all_popens = []
class MockPopen(object):
def __init__(self, args, stdout=None, stdin=None, stderr=None):
all_popens.append(self)
self.args = args
self.byte_collection = MockBytes()
self.stdin = self.byte_collection
self.stdout = self.byte_collection
self.stderr = self.byte_collection
pass
monkeypatch.setattr(helpers, 'Popen', MockPopen)
return all_popens
उदाहरण परीक्षण, पहले से शुरू होना चाहिए test_docker.py
फ़ाइल में उपसर्ग test_
def test_docker_install():
p = Popen(['which', 'docker'], stdout=PIPE, stderr=PIPE)
result = p.stdout.read()
assert 'bin/docker' in result
def test_copy_file_to_docker(all_popens):
result = copy_file_to_docker('asdf', 'asdf')
collected_popen = all_popens.pop()
mock_read, mock_write, mock_close = collected_popen.byte_collection.get_all_mock_bytes()
assert mock_read
assert result.args == ['docker', 'cp', 'asdf', 'something_cont:asdf']
def test_docker_exec_something(all_popens):
docker_exec_something(something_file_string)
collected_popen = all_popens.pop()
mock_read, mock_write, mock_close = collected_popen.byte_collection.get_all_mock_bytes()
assert len(mock_read) == 3
something_template_stdin = mock_write[0][1][0]
these = [os.environ['USER'], os.environ['password_prod'], 'table_name_here', 'test_vdm', 'col_a', 'col_b', '/tmp/test.tsv']
assert all([x in something_template_stdin for x in these])
एक समय में एक परीक्षण चल रहा है:
py.test -k test_docker_install tests
py.test -k test_copy_file_to_docker tests
py.test -k test_docker_exec_something tests
tests
फ़ोल्डर में सभी परीक्षण चल रहे हैं:
py.test -k test_ tests