ইনপুট আর্গুমেন্টের উপর ভিত্তি করে পাইথন ফাংশনকে উপহাস করা হচ্ছে


150

আমরা অজগর জন্য মক ব্যবহার করছি কিছুক্ষণের জন্য।

এখন, আমাদের একটি পরিস্থিতি রয়েছে যেখানে আমরা কোনও ফাংশনকে উপহাস করতে চাই

def foo(self, my_param):
    #do something here, assign something to my_result
    return my_result

সাধারণত, এটির উপহাস করার উপায়টি হ'ল (foo অবজেক্টের অংশ হিসাবে ধরে নেওয়া)

self.foo = MagicMock(return_value="mocked!")

এমনকি, যদি আমি ফো ()) কয়েকবার কল করি তবে আমি ব্যবহার করতে পারি

self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])

এখন, আমি এমন পরিস্থিতির মুখোমুখি হয়েছি যেখানে ইনপুট প্যারামিটারের একটি নির্দিষ্ট মান থাকলে আমি একটি স্থির মানটি ফিরিয়ে দিতে চাই। সুতরাং যদি "মাই_প্যারাম" "কিছু" এর সমান হয় তবে আমি "আমার_কুল_মোক" ফিরিয়ে দিতে চাই

এটি অজগরটির জন্য মকিতোতে উপলব্ধ বলে মনে হচ্ছে

when(dummy).foo("something").thenReturn("my_cool_mock")

আমি কীভাবে সাফল্যের সাথে মকের সাথে একই অর্জন করতে পারি তা অনুসন্ধান করছি?

কোন ধারনা?


2
হতে এই উত্তরটি সাহায্য করবে - stackoverflow.com/a/7665754/234606
naiquevin

@নাউইকোভিন এই সমস্যাটি সাথীদের জন্য পুরোপুরি সমাধান করে, ধন্যবাদ!
জুয়ান আন্তোনিও গোমেজ মরিয়ানো

আপনি পাইথনের সাথে মক্টোটিও ব্যবহার করতে পারবেন এমন আমার ধারণা ছিল না, এটির জন্য +1!
বেন

যদি আপনার প্রকল্প পাইটস্ট ব্যবহার করে তবে এইরকম উদ্দেশ্যে আপনি লাভবান করতে পারেন monkeypatch। মনকিপ্যাচটি "পরীক্ষার জন্য এই ফাংশনটি প্রতিস্থাপন করুন" এর জন্য আরও বেশি, যখন আপনি যখন ম্যাকটি ব্যবহার করেন তখন আপনি এটি পরীক্ষা করে দেখতে চান mock_callsবা এটি কী বলা হয়েছিল সে সম্পর্কে দৃ about়তা জানাতে পারেন। উভয়ের জন্য একটি জায়গা রয়েছে এবং আমি প্রায়শই একটি প্রদত্ত পরীক্ষার ফাইলে বিভিন্ন সময়ে উভয়ই ব্যবহার করি।
ড্রিফ্যাচ্যাচার

উত্তর:


187

যদি side_effectকোনও ফাংশন হয় তবে সেই ফাংশনটি যা কিছু দেয় তা মাক রিটার্নকে কল করে। side_effectফাংশন উপহাস হিসাবে একই আর্গুমেন্ট সহ বলা হয়। এটি আপনাকে ইনপুটের উপর ভিত্তি করে গতিশীলভাবে কলটির রিটার্ন মানকে আলাদা করতে দেয়:

>>> def side_effect(value):
...     return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]

http://www.voidspace.org.uk/python/mock/mock.html#calling


25
উত্তরটি সহজ করার জন্য, আপনি কী পার্শ্ব_আফেক্ট ফাংশনটির নামকরণ করতে পারেন অন্য কিছু? (আমি জানি, আমি জানি, এটি বেশ সহজ, তবে পাঠ্যতা উন্নত করে ফাংশন নাম এবং পরম নাম পৃথক হওয়া :)
হুয়ান আন্তোনিও গোমেজ মরিয়ানো

6
@ জুয়ান অ্যান্টোনিও গোমেজ মোরিয়ানো আমি পারলাম, তবে এই ক্ষেত্রে আমি সরাসরি ডকুমেন্টেশনটি উদ্ধৃত করছি, সুতরাং বিশেষভাবে ভাঙ্গা না থাকলে উদ্ধৃতি সম্পাদনা করতে আমি কিছুটা তত্পর।
অ্যাম্বার

এবং এই সমস্ত বছর পরে কেবল side effect
পেডেন্টিক

7
@ যদি তারা নামটির বিষয়ে অভিযোগ করে না CallableMixin.side_effect, তবে উদাহরণে বর্ণিত পৃথক ফাংশনটির একই নাম রয়েছে has
অরেঞ্জডগ

48

পাইথন মক অবজেক্টে একাধিকবার কলিত পদ্ধতিতে নির্দেশিত হিসাবে

একটি সমাধান হ'ল আমার নিজের লেখা_আফেক্ট লিখুন

def my_side_effect(*args, **kwargs):
    if args[0] == 42:
        return "Called with 42"
    elif args[0] == 43:
        return "Called with 43"
    elif kwargs['foo'] == 7:
        return "Foo is seven"

mockobj.mockmethod.side_effect = my_side_effect

কৌতুকটি করে


2
এটি নির্বাচিত উত্তরের চেয়ে আমার কাছে আরও স্পষ্ট করে তুলেছে, তাই আপনার নিজের প্রশ্নের উত্তর দেওয়ার জন্য আপনাকে ধন্যবাদ :)
লুকা বেজারেরা

15

পার্শ্ব প্রতিক্রিয়া একটি ফাংশন নেয় (যা ল্যাম্বডা ফাংশনও হতে পারে ), তাই সাধারণ ক্ষেত্রে আপনি ব্যবহার করতে পারেন:

m = MagicMock(side_effect=(lambda x: x+1))

4

আমি " ইনপুট আর্গুমেন্টের উপর ভিত্তি করে কোনও ফাংশনকে কীভাবে উপহাস করব " সন্ধান করে এখানেই শেষ হয়েছি এবং অবশেষে আমি একটি সাধারণ অক্স ফাংশন তৈরি করে সমাধান করেছি:

def mock_responses(responses, default_response=None):
  return lambda input: responses[input] if input in responses else default_response

এখন:

my_mock.foo.side_effect = mock_responses(
  {
    'x': 42, 
    'y': [1,2,3]
  })
my_mock.goo.side_effect = mock_responses(
  {
    'hello': 'world'
  }, 
  default_response='hi')
...

my_mock.foo('x') # => 42
my_mock.foo('y') # => [1,2,3]
my_mock.foo('unknown') # => None

my_mock.goo('hello') # => 'world'
my_mock.goo('ey') # => 'hi'

আশা করি এটি কারও সাহায্য করবে!


2

এছাড়াও আপনি ব্যবহার করতে পারেন partialথেকে functoolsআপনি একটি ফাংশন যা পরামিতি নেয় ব্যবহার করতে চান কিন্তু ফাংশন আপনি উপহাস করা হয় না। যেমন:

def mock_year(year):
    return datetime.datetime(year, 11, 28, tzinfo=timezone.utc)
@patch('django.utils.timezone.now', side_effect=partial(mock_year, year=2020))

এটি এমন কলযোগ্য ফিরে আসবে যা প্যারামিটারগুলি গ্রহণ করে না (যেমন জ্যাঙ্গোর টাইমজোন.নো ()) তবে আমার মক_ইয়ার ফাংশনটি তা করে।


এই মার্জিত সমাধানের জন্য ধন্যবাদ। আমি যুক্ত করতে চাই যে আপনার মূল ফাংশনে অতিরিক্ত প্যারামিটার থাকলে তাদেরকে আপনার প্রোডাকশন কোডে কীওয়ার্ড সহ কল ​​করতে হবে বা এই পদ্ধতিটি কাজ করবে না। আপনি ত্রুটি পাবেন: got multiple values for argument
এরিক কালকোকেন

1

এটি করার অন্য উপায়টি দেখানোর জন্য:

def mock_isdir(path):
    return path in ['/var/log', '/var/log/apache2', '/var/log/tomcat']

with mock.patch('os.path.isdir') as os_path_isdir:
    os_path_isdir.side_effect = mock_isdir

1

আপনি এটি ব্যবহার করতে পারেন @mock.patch.object:

ধরা যাক একটি মডিউল একটি ডাটাবেস থেকে পড়তে my_module.pyব্যবহার pandasকরে এবং আমরা এই মডিউলটিকে উপহাস করার pd.read_sql_tableপদ্ধতি (যা table_nameযুক্তি হিসাবে গ্রহণ করে) দ্বারা পরীক্ষা করতে চাই ।

আপনি যা করতে পারেন তা হল (আপনার পরীক্ষার অভ্যন্তরে) এমন একটি db_mockপদ্ধতি তৈরি করা যা প্রদত্ত যুক্তির উপর নির্ভর করে বিভিন্ন বস্তু ফেরত দেয়:

def db_mock(**kwargs):
    if kwargs['table_name'] == 'table_1':
        # return some DataFrame
    elif kwargs['table_name'] == 'table_2':
        # return some other DataFrame

আপনার পরীক্ষার কার্যক্রমে আপনি তা করতে পারেন:

import my_module as my_module_imported

@mock.patch.object(my_module_imported.pd, "read_sql_table", new_callable=lambda: db_mock)
def test_my_module(mock_read_sql_table):
    # You can now test any methods from `my_module`, e.g. `foo` and any call this 
    # method does to `read_sql_table` will be mocked by `db_mock`, e.g.
    ret = my_module_imported.foo(table_name='table_1')
    # `ret` is some DataFrame returned by `db_mock`

1

যদি আপনি "ইনপুট প্যারামিটারের একটি নির্দিষ্ট মান থাকে তখন একটি নির্দিষ্ট মানটি ফিরিয়ে দিতে চান" , সম্ভবত আপনার এমনকি একটি উপহাসের প্রয়োজন নেই এবং dictতার getপদ্ধতির সাথে এটি ব্যবহার করতে পারেন :

foo = {'input1': 'value1', 'input2': 'value2'}.get

foo('input1')  # value1
foo('input2')  # value2

এটি যখন আপনার জাল এর আউটপুট ইনপুট ম্যাপিং হয় ভাল কাজ করে । এটি একটি যখন ফাংশন ইনপুটের আমি ব্যবহার করার সুপারিশ করছি side_effectঅনুযায়ী অ্যাম্বার এর উত্তর।

যদি আপনি সংরক্ষণ করতে চান এছাড়াও আপনি উভয়ের একটি সমন্বয় ব্যবহার করতে পারেন Mock'এর ক্ষমতা ( assert_called_once, call_countইত্যাদি):

self.mock.side_effect = {'input1': 'value1', 'input2': 'value2'}.get

এটা খুব চালাক।
এমিলার

-6

আমি জানি এটি বেশ পুরানো প্রশ্ন, পাইথন ল্যামডবা ব্যবহার করে উন্নতি হিসাবে সহায়তা করতে পারে

self.some_service.foo.side_effect = lambda *args:"Called with 42" \
            if args[0] == 42 \
            else "Called with 42" if args[0] == 43 \
            else "Called with 43" if args[0] == 43 \
            else "Called with 45" if args[0] == 45 \
            else "Called with 49" if args[0] == 49 else None
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.