জোর দিয়ে বলুন যে পাইথন ইউনিট পরীক্ষায় একটি পদ্ধতি আহ্বান করা হয়েছিল


91

ধরুন পাইথন ইউনিট পরীক্ষায় আমার কাছে নিম্নলিখিত কোড রয়েছে:

aw = aps.Request("nv1")
aw2 = aps.Request("nv2", aw)

aw.Clear()পরীক্ষার দ্বিতীয় লাইনের সময় কোনও নির্দিষ্ট পদ্ধতি (আমার ক্ষেত্রে ) ডেকে আনা হয়েছিল এমনটি বলার কী সহজ উপায় আছে ? যেমন এখানে কিছু আছে:

#pseudocode:
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw))

উত্তর:


150

আমি এর জন্য মোক (যা এখন ইউনিটেস্ট.পায় ৩.৩ + তে মক) ব্যবহার করি:

from mock import patch
from PyQt4 import Qt


@patch.object(Qt.QMessageBox, 'aboutQt')
def testShowAboutQt(self, mock):
    self.win.actionAboutQt.trigger()
    self.assertTrue(mock.called)

আপনার ক্ষেত্রে এটি দেখতে এরকম হতে পারে:

import mock
from mock import patch


def testClearWasCalled(self):
   aw = aps.Request("nv1")
   with patch.object(aw, 'Clear') as mock:
       aw2 = aps.Request("nv2", aw)

   mock.assert_called_with(42) # or mock.assert_called_once_with(42)

মক বেশ কয়েকটি দরকারী বৈশিষ্ট্য সমর্থন করে, যার মধ্যে কোনও বস্তু বা মডিউল প্যাচ করার উপায়গুলি পাশাপাশি সঠিক জিনিসটি বলা হয়েছিল কিনা তা পরীক্ষা করা ইত্যাদি etc.

ক্যাভেট সম্রাট! (ক্রেতা হুঁশিয়ার!)

যদি আপনি ভুল পরীক্ষা করে assert_called_with( assert_called_onceবা assert_called_wiht) চালিয়ে যেতে পারেন তবে আপনার পরীক্ষাটি চলতে পারে, কারণ মক ভাবেন যে এটি একটি বিদ্রূপযুক্ত ফাংশন এবং আপনি যদি না ব্যবহার করেন তবে সুখের সাথে চলতে হবে autospec=true। আরও তথ্যের জন্য assert_called_once পড়ুন: হুমকি বা মেনেস


4
বিস্ময়কর মক মডিউলটি দিয়ে আমার বিশ্বকে আলোকিত করার জন্য +1।
রন কোহেন

@ রনকোহেন: হ্যাঁ, এটি বেশ আশ্চর্যজনক এবং সর্বদা আরও ভাল হয়ে উঠছে। :)
মাককে

4
উপহাসটি ব্যবহার করার উপায় অবশ্যই, আমি assert_called_once ব্যবহারের বিরুদ্ধে পরামর্শ দেই, কেবল অস্তিত্ব নেই :)
ফেলিকসিকিউ

এটি পরবর্তী সংস্করণগুলিতে সরানো হয়েছে। আমার পরীক্ষাগুলি এখনও এটি ব্যবহার করছে are :)
মাকেকে

4
অটোস্পেক = এটি যে কোনও বিদ্রূপযুক্ত অবজেক্টের জন্য সত্য তা ব্যবহার করা কতটা সহায়ক তা পুনরুক্ত করার মতো কারণ আপনি যদি দৃ the়পদ পদ্ধতিটি ভুল বানান করে থাকেন তবে তা আপনাকে সত্যই কামড়ায়।
rgilligan

30

হ্যাঁ যদি আপনি পাইথন ৩.৩+ ব্যবহার করেন। আপনি অন্তর্নির্মিত unittest.mockপদ্ধতিতে জোর দেওয়া পদ্ধতি ব্যবহার করতে পারেন । পাইথন ২.6+ এর জন্য রোলিং ব্যাকপোর্ট ব্যবহার করুন Mockযা একই জিনিস।

আপনার ক্ষেত্রে এখানে একটি দ্রুত উদাহরণ দেওয়া হয়েছে:

from unittest.mock import MagicMock
aw = aps.Request("nv1")
aw.Clear = MagicMock()
aw2 = aps.Request("nv2", aw)
assert aw.Clear.called

14

আমি অন্তর্নির্মিত কিছু সম্পর্কে অবগত নই। এটি প্রয়োগ করা বেশ সহজ:

class assertMethodIsCalled(object):
    def __init__(self, obj, method):
        self.obj = obj
        self.method = method

    def called(self, *args, **kwargs):
        self.method_called = True
        self.orig_method(*args, **kwargs)

    def __enter__(self):
        self.orig_method = getattr(self.obj, self.method)
        setattr(self.obj, self.method, self.called)
        self.method_called = False

    def __exit__(self, exc_type, exc_value, traceback):
        assert getattr(self.obj, self.method) == self.called,
            "method %s was modified during assertMethodIsCalled" % self.method

        setattr(self.obj, self.method, self.orig_method)

        # If an exception was thrown within the block, we've already failed.
        if traceback is None:
            assert self.method_called,
                "method %s of %s was not called" % (self.method, self.obj)

class test(object):
    def a(self):
        print "test"
    def b(self):
        self.a()

obj = test()
with assertMethodIsCalled(obj, "a"):
    obj.b()

এর জন্য প্রয়োজন যে অবজেক্টটি নিজেই self.b পরিবর্তন করবে না, যা প্রায় সর্বদা সত্য।


আমি বলেছিলাম যে আমার পাইথনটি মরিচা, যদিও আমি এটি সমাধান করেছিলাম তা নিশ্চিত করার জন্য আমার সমাধানটি পরীক্ষা করেছিলাম :-) আমি 2.5 সংস্করণের আগে পাইথনকে অভ্যন্তরীণ করেছিলাম, বাস্তবে আমি কখনও উল্লেখযোগ্য পাইথনের জন্য 2.5 ব্যবহার করি নি কারণ আমাদের লিবিব সামঞ্জস্যের জন্য ২.৩ এ জমা করতে হয়েছিল। আপনার সমাধানটি পর্যালোচনা করতে গিয়ে আমি effbot.org/zone/python-with-statement.htm কে একটি সুন্দর পরিষ্কার বিবরণ হিসাবে পেয়েছি । আমি বিনীতভাবে পরামর্শ দেব যে আমার পদ্ধতিরটি দেখতে আরও ছোট দেখায় এবং প্রয়োগ করা সহজ হতে পারে যদি আপনি "এস" এর সাথে নেস্ট করার চেয়ে এক পয়েন্টের বেশি লগিং চান। আপনার কোনও বিশেষ সুবিধা রয়েছে কিনা তা আমি আপনাকে সত্যিই ব্যাখ্যা করতে চাই।
অ্যান্ডি ডেন্ট

@ অ্যান্ডি: আপনার উত্তরটি ছোট কারণ এটি আংশিক: এটি আসলে ফলাফলগুলি পরীক্ষা করে না, এটি পরীক্ষার পরে মূল ফাংশনটি পুনরুদ্ধার করে না যাতে আপনি অবজেক্টটি ব্যবহার করা চালিয়ে যেতে পারেন, এবং আপনাকে সমস্ত বার বার কোড লিখতে হবে এটি আবার প্রতিবার আপনি একটি পরীক্ষা লিখুন। সমর্থন কোডের লাইনের সংখ্যা গুরুত্বপূর্ণ নয়; এই শ্রেণিটি তার নিজস্ব টেস্টিং মডিউলে যায়, কোনও ডাস্ট্রিংয়ের সাথে ইনলাইন নয় - এটি প্রকৃত পরীক্ষায় কোডের এক বা দুটি লাইন নেয়।
গ্লেন মেনার্ড 20

6

হ্যাঁ, আমি আপনাকে রূপরেখা দিতে পারি তবে আমার পাইথনটি কিছুটা মরিচা এবং আমি বিশদ বিবরণ দিতে খুব ব্যস্ত।

মূলত, আপনাকে সেই পদ্ধতিতে একটি প্রক্সি স্থাপন করতে হবে যা মূলটিকে কল করবে, যেমন:

 class fred(object):
   def blog(self):
     print "We Blog"


 class methCallLogger(object):
   def __init__(self, meth):
     self.meth = meth

   def __call__(self, code=None):
     self.meth()
     # would also log the fact that it invoked the method

 #example
 f = fred()
 f.blog = methCallLogger(f.blog)

এই স্ট্যাকওভারফ্লো উত্তর সম্পর্কে callable সাহায্য করতে পারে আপনি উপরের বুঝতে।

আরো বিস্তারিত:

যদিও উত্তরটি গৃহীত হয়েছিল, গ্লেনের সাথে আকর্ষণীয় আলোচনা এবং কয়েক মিনিট ফ্রি থাকার কারণে, আমি আমার উত্তরটি আরও বাড়িয়ে দিতে চেয়েছিলাম:

# helper class defined elsewhere
class methCallLogger(object):
   def __init__(self, meth):
     self.meth = meth
     self.was_called = False

   def __call__(self, code=None):
     self.meth()
     self.was_called = True

#example
class fred(object):
   def blog(self):
     print "We Blog"

f = fred()
g = fred()
f.blog = methCallLogger(f.blog)
g.blog = methCallLogger(g.blog)
f.blog()
assert(f.blog.was_called)
assert(not g.blog.was_called)

সুন্দর আমি methCallLogger এ একটি কল গণনা যুক্ত করেছি যাতে আমি এতে দৃ as়তা রাখতে পারি।
হিথকে চিহ্নিত করুন

এটি আমি সরবরাহ করে নিখুঁত, স্ব-অন্তর্ভুক্ত সমাধানের উপরে? সিরিয়াসলি?
গ্লেন মেইনার্ড

@ গ্লেন আমি পাইথনে খুব নতুন - সম্ভবত আপনার চেয়ে ভাল এটি - আমি এখনও এগুলি সব বুঝতে পারি না। আমি পরে চেষ্টা করে কিছুটা সময় ব্যয় করব।
মার্ক হিথ

এটি এখন পর্যন্ত সবচেয়ে সহজ এবং সহজ-বোঝার উত্তর। সত্যিই দুর্দান্ত কাজ!
ম্যাট মেসারস্মিথ

4

আপনি ম্যাকুয়ালিaw.Clear বা পাইমক্সের মতো পরীক্ষামূলক কাঠামো ব্যবহার করে ব্যঙ্গ করতে পারেন । ম্যানুয়ালি, আপনি এটির মতো কিছু ব্যবহার করে এটি করতে চাইবেন:

class MyTest(TestCase):
  def testClear():
    old_clear = aw.Clear
    clear_calls = 0
    aw.Clear = lambda: clear_calls += 1
    aps.Request('nv2', aw)
    assert clear_calls == 1
    aw.Clear = old_clear

পাইমক্স ব্যবহার করে, আপনি এটি এটি করতে চাই:

class MyTest(mox.MoxTestBase):
  def testClear():
    aw = self.m.CreateMock(aps.Request)
    aw.Clear()
    self.mox.ReplayAll()
    aps.Request('nv2', aw)

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