মক পদ্ধতিতে ক্রমাগত কলগুলি যুক্ত করা


175

মোক একটি সহায়ক assert_called_with()পদ্ধতি আছে । তবে যতদূর আমি এটি বুঝতে পারি কেবল কোনও পদ্ধতির শেষ কলটি পরীক্ষা করে ।
যদি আমার কাছে এমন কোড রয়েছে যা উপহাসের পদ্ধতিটিকে পরপর 3 বার কল করে, প্রতিটি সময় বিভিন্ন পরামিতি সহ, আমি কীভাবে এই নির্দিষ্ট 3 টি কলগুলিকে তাদের নির্দিষ্ট পরামিতিগুলির সাথে যুক্ত করতে পারি?

উত্তর:


179

assert_has_calls এই সমস্যার আরেকটি পদ্ধতি।

ডক্স থেকে:

assert_has_call (কল, যে কোনও_অর্ডার = মিথ্যা)

উপস্থাপন করুন নির্দিষ্ট কলগুলির সাথে মককে ডাকা হয়েছে। মক_কলের তালিকা কলগুলির জন্য চেক করা হয়।

যদি কোনও_অর্ডার মিথ্যা (ডিফল্ট) হয় তবে কলগুলি অবশ্যই ক্রমযুক্ত হবে। নির্দিষ্ট কলগুলির আগে বা পরে অতিরিক্ত কলগুলি আসতে পারে।

যদি কোনও_অর্ডারটি সত্য হয় তবে কলগুলি যে কোনও ক্রমে হতে পারে তবে সেগুলি অবশ্যই মক_কলে উপস্থিত থাকতে হবে।

উদাহরণ:

>>> from unittest.mock import call, Mock
>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)

সূত্র: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_has_calls


9
কিছুটা অদ্ভুত তারা একটি নতুন "কল" টাইপ যুক্ত করতে বেছে নিয়েছিল যার জন্য তারা কেবল একটি তালিকা বা একটি
টুপল

@ জাজাপজ এটি সাবক্লাসগুলি tuple: isinstance(mock.call(1), tuple)দেয় True। তারা কিছু পদ্ধতি এবং গুণাবলী যুক্ত করেছে।
jpmc26

13
মোক এর প্রাথমিক সংস্করণগুলি একটি সরল tuple ব্যবহার করেছে, তবে এটি ব্যবহারে বিশ্রী হতে দেখা যাচ্ছে। প্রতিটি ফাংশন কলটি (আরগস, কাওয়ার্গস) একটি দ্বিগুণ প্রাপ্তি পায়, সুতরাং "foo (123)" টি সঠিকভাবে বলা হয়েছিল তা পরীক্ষা করতে আপনাকে "mock.call_args == ((123,), {})" দাবি করতে হবে, যা "কল (123)" এর সাথে তুলনা করা এক মুখী
জোনাথন হার্টলি

প্রতিটি কল যখন আপনি আলাদা ফেরতের মান আশা করেন আপনি কি করবেন?
কোডভিথপ্রাইড

2
@ কোড উইথপ্রাইড এটি আরও একটি কাজের জন্য দেখায়side_effect
পিগুইরাস

108

সাধারণত, আমি কলগুলির ক্রমটির বিষয়ে চিন্তা করি না, কেবল সেগুলি ঘটেছিল। সেক্ষেত্রে আমি assert_any_callএকটি দৃser ়তার সাথে একত্রিত হই call_count

>>> import mock
>>> m = mock.Mock()
>>> m(1)
<Mock name='mock()' id='37578160'>
>>> m(2)
<Mock name='mock()' id='37578160'>
>>> m(3)
<Mock name='mock()' id='37578160'>
>>> m.assert_any_call(1)
>>> m.assert_any_call(2)
>>> m.assert_any_call(3)
>>> assert 3 == m.call_count
>>> m.assert_any_call(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "[python path]\lib\site-packages\mock.py", line 891, in assert_any_call
    '%s call not found' % expected_string
AssertionError: mock(4) call not found

আমি একক পদ্ধতিতে পাস করা কলগুলির একটি বৃহত তালিকার চেয়ে পড়া এবং বুঝতে সহজ হওয়া এই পদ্ধতিতে এটি করি find

যদি আপনি অর্ডার সম্পর্কে যত্নশীল হন বা আপনি একাধিক অভিন্ন কল আশা করেন তবে assert_has_callsএটি আরও উপযুক্ত হতে পারে।

সম্পাদন করা

যেহেতু আমি এই উত্তরটি পোস্ট করেছি, তাই আমি সাধারণভাবে পরীক্ষার বিষয়ে আমার পদ্ধতির পুনর্বিবেচনা করেছি। আমি মনে করি এটি উল্লেখ করার মতো যে আপনার পরীক্ষাটি যদি এই জটিল হয়ে উঠতে পারে তবে আপনি অনুপযুক্ত পরীক্ষা করতে পারেন বা ডিজাইনের সমস্যা হতে পারে। মকস একটি অবজেক্ট ওরিয়েন্টেড ডিজাইনে আন্তঃ-বস্তু যোগাযোগের পরীক্ষার জন্য ডিজাইন করা হয়েছে। যদি আপনার নকশাটি আপত্তিমুখী না হয় (যেমন আরও পদ্ধতিগত বা কার্যকরী হিসাবে) তবে মক সম্পূর্ণরূপে অনুপযুক্ত হতে পারে। পদ্ধতির অভ্যন্তরে আপনার খুব বেশি পরিমাণে চলতে পারে, বা আপনি সম্ভবত অভ্যন্তরীণ বিশদটি পরীক্ষা করছেন যা সেরা আনমক করা আছে। আমি যখন এই কোডটিতে উল্লিখিত কৌশলটি তৈরি করেছিলাম যখন আমার কোডটি খুব বেশি অবজেক্ট ভিত্তিক ছিল না এবং আমি বিশ্বাস করি যে আমি অভ্যন্তরীণ বিশদগুলিও পরীক্ষা করে দেখছিলাম যা ভালভাবে আনমক করা হত।


@ jpmc26 আপনি কি নিজের সম্পাদনার বিষয়ে আরও বিস্তারিত বলতে পারবেন? 'সেরা বাম আনমকড' বলতে কী বোঝ? কোনও পদ্ধতির মধ্যে যদি কল করা হয়ে থাকে তবে আপনি কীভাবে পরীক্ষা করবেন
otgw

@ মেমো প্রায়শই আসল পদ্ধতিটি কল করা ভাল। অন্য পদ্ধতিটি যদি ভেঙে যায় তবে এটি পরীক্ষাটি ভেঙে দিতে পারে, তবে এড়ানোর মান একটি সহজ, আরও রক্ষণাবেক্ষণযোগ্য পরীক্ষা করার মানের চেয়ে ছোট smaller বিদ্রূপের সবচেয়ে ভাল সময়গুলি যখন অন্য পদ্ধতির বাইরের কলটি আপনি যা পরীক্ষা করতে চান তা হয় (সাধারণত, এর অর্থ এটিতে কোনওরকম ফলাফল পাস করা হয় এবং পরীক্ষার অধীনে থাকা কোডটি কোনও ফল দেয় না)) বা অন্য পদ্ধতি বাহ্যিক নির্ভরতা (ডাটাবেস, ওয়েবসাইট) আপনি মুছে ফেলতে চান। (প্রযুক্তিগতভাবে, শেষ
কেসটি

আপনি যখন নির্ভরতা ইনজেকশন বা রানটাইম কৌশল চয়ন করার কোনও অন্য পদ্ধতি এড়াতে চান তখন @ jpmc26 বিদ্রূপটি কার্যকর। যেমন আপনি উল্লেখ করেছেন, পদ্ধতির অভ্যন্তরীণ যুক্তি পরীক্ষা করা, বাহ্যিক পরিষেবাগুলি কল না করে এবং আরও গুরুত্বপূর্ণ, পরিবেশ সম্পর্কে সচেতন না হয়ে (ভাল কোডের জন্য কোনও সংখ্যা নেই do() if TEST_ENV=='prod' else dont()), আপনার পরামর্শ মতো উপহাস করার মাধ্যমে সহজেই অর্জন করা যায়। এর একটি পার্শ্ব প্রতিক্রিয়া হ'ল প্রতি সংস্করণে পরীক্ষা চালিয়ে যাওয়া (গুগল অনুসন্ধান এপিআই ভি 1 এবং ভি 2 এর মধ্যে কোড পরিবর্তনগুলি বলুন, আপনার কোড যাই হোক না কেন সংস্করণ 1 পরীক্ষা করবে)
ড্যানিয়েল ডুবভস্কি

@ ড্যানিয়েলডুবভস্কি আপনার পরীক্ষার বেশিরভাগটি ইনপুট / আউটপুট ভিত্তিক হওয়া উচিত। এটি সর্বদা সম্ভব নয়, তবে বেশিরভাগ সময় এটি সম্ভব না হলে আপনার সম্ভবত কোনও ডিজাইনের সমস্যা রয়েছে। যখন আপনার কিছু মান দরকার হয় যা সাধারণত কোডের অন্য টুকরো থেকে আসে এবং আপনি নির্ভরতা কাটাতে চান, তখন একটি স্টাব সাধারণত করবে। মকগুলি কেবল তখনই প্রয়োজন হয় যখন আপনাকে যাচাই করতে হবে যে কোনও স্টেট মডিফাইং ফাংশন (সম্ভবত কোনও রিটার্নের মান না দিয়ে) ডাকা হয়েছে। (একটি মোক এবং স্টাবের মধ্যে পার্থক্য হ'ল আপনি স্টাবের সাথে কোনও কলকে দৃsert়তার সাথে বলেন না)) যেখানে স্টাবগুলি করবে এমন মক ব্যবহার করা আপনার পরীক্ষাগুলি কম রক্ষণাবেক্ষণযোগ্য করে তোলে।
jpmc26

@ jpmc26 একটি বাহ্যিক পরিষেবাকে এক ধরণের আউটপুট বলছে না? অবশ্যই আপনি যে কোডটি বার্তাটি প্রেরণ করতে হবে তা তৈরি করে এবং কল প্যারামগুলি দৃser় করার পরিবর্তে এটি পরীক্ষা করতে পারে তবে আইএমএইচও, এটি প্রায় একই রকম। আপনি কীভাবে বাহ্যিক এপিআই এর কলিংকে আবার ডিজাইনিং করার পরামর্শ দিবেন? আমি সম্মত হই যে বিদ্রূপকে সর্বনিম্ন কমাতে হবে, সমস্ত আমি বলছি আপনি যে যুক্তিটি প্রত্যাশা অনুযায়ী আচরণ করছেন তা নিশ্চিত করার জন্য আপনি বাহ্যিক পরিষেবাগুলিতে প্রেরিত ডেটা পরীক্ষা করতে পারবেন না।
ড্যানিয়েল দুবভস্কি

46

পূর্ববর্তী পদ্ধতি কলগুলির সাথে পরামিতিগুলির তুলনা করতে আপনি Mock.call_args_listবৈশিষ্ট্যটি ব্যবহার করতে পারেন । এটি Mock.call_countবৈশিষ্ট্যের সাথে একত্রে আপনাকে সম্পূর্ণ নিয়ন্ত্রণ দেওয়া উচিত।


9
assert_has_calls ()?
বাভাজা

5
assert_has_callsকেবলমাত্র প্রত্যাশিত কলগুলি করা হয়েছে কিনা তা যাচাই করে তবে এটি কেবলমাত্র যদি হয়।
নীল রঙের

17

আমাকে সর্বদা সময় এবং সময়টিকে একবার দেখতে হবে, তাই আমার উত্তর এখানে is


একই শ্রেণীর বিভিন্ন অবজেক্টে একাধিক পদ্ধতি কল আহ্বান করা

মনে করুন আমাদের একটি ভারী দায়িত্বের ক্লাস রয়েছে (যা আমরা উপহাস করতে চাই):

In [1]: class HeavyDuty(object):
   ...:     def __init__(self):
   ...:         import time
   ...:         time.sleep(2)  # <- Spends a lot of time here
   ...:     
   ...:     def do_work(self, arg1, arg2):
   ...:         print("Called with %r and %r" % (arg1, arg2))
   ...:  

এখানে কিছু কোড যা HeavyDutyশ্রেণীর দুটি উদাহরণ ব্যবহার করে :

In [2]: def heavy_work():
   ...:     hd1 = HeavyDuty()
   ...:     hd1.do_work(13, 17)
   ...:     hd2 = HeavyDuty()
   ...:     hd2.do_work(23, 29)
   ...:    


এখন, heavy_workফাংশনটির জন্য এখানে একটি পরীক্ষার কেস দেওয়া হয়েছে :

In [3]: from unittest.mock import patch, call
   ...: def test_heavy_work():
   ...:     expected_calls = [call.do_work(13, 17),call.do_work(23, 29)]
   ...:     
   ...:     with patch('__main__.HeavyDuty') as MockHeavyDuty:
   ...:         heavy_work()
   ...:         MockHeavyDuty.return_value.assert_has_calls(expected_calls)
   ...:  

আমরা HeavyDutyক্লাস নিয়ে ঠাট্টা করছি MockHeavyDuty। প্রতিটি ইভেন্ট থেকে আগত পদ্ধতি কলগুলি জোড় করার HeavyDutyজন্য আমাদের MockHeavyDuty.return_value.assert_has_callsপরিবর্তে উল্লেখ করতে হবে MockHeavyDuty.assert_has_calls। তদতিরিক্ত, তালিকায় expected_callsআমাদের কোন পদ্ধতির নামটি কল করতে আগ্রহী তা নির্দিষ্ট করতে হবে। সুতরাং আমাদের তালিকা কল থেকে তৈরি করা হয়েছে call.do_work, কেবল বিপরীতে call

পরীক্ষার কেসটি অনুশীলন করা আমাদের দেখায় যে এটি সফল:

In [4]: print(test_heavy_work())
None


আমরা যদি heavy_workফাংশনটি সংশোধন করি তবে পরীক্ষাটি ব্যর্থ হয় এবং একটি সহায়ক ত্রুটি বার্তা উত্পন্ন করে:

In [5]: def heavy_work():
   ...:     hd1 = HeavyDuty()
   ...:     hd1.do_work(113, 117)  # <- call args are different
   ...:     hd2 = HeavyDuty()
   ...:     hd2.do_work(123, 129)  # <- call args are different
   ...:     

In [6]: print(test_heavy_work())
---------------------------------------------------------------------------
(traceback omitted for clarity)

AssertionError: Calls not found.
Expected: [call.do_work(13, 17), call.do_work(23, 29)]
Actual: [call.do_work(113, 117), call.do_work(123, 129)]


একটি ফাংশনে একাধিক কল জোর দেওয়া

উপরের সাথে বিপরীতে, এখানে একটি উদাহরণ যা দেখায় যে কোনও ফাংশনে একাধিক কলকে কীভাবে উপহাস করা যায়:

In [7]: def work_function(arg1, arg2):
   ...:     print("Called with args %r and %r" % (arg1, arg2))

In [8]: from unittest.mock import patch, call
   ...: def test_work_function():
   ...:     expected_calls = [call(13, 17), call(23, 29)]    
   ...:     with patch('__main__.work_function') as mock_work_function:
   ...:         work_function(13, 17)
   ...:         work_function(23, 29)
   ...:         mock_work_function.assert_has_calls(expected_calls)
   ...:    

In [9]: print(test_work_function())
None


দুটি প্রধান পার্থক্য আছে। প্রথমটি হ'ল কোনও ফাংশনটিকে উপহাস করার সময় আমরা ব্যবহারের callপরিবর্তে আমাদের প্রত্যাশিত কলগুলি সেটআপ করি call.some_method। দ্বিতীয় যে আমরা কল assert_has_callsউপর mock_work_functionপরিবর্তে উপর, mock_work_function.return_value

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.