ล้อเลียนชั้นเรียน: Mock () หรือ patch ()?


116

ฉันใช้การเยาะเย้ยกับ Python และสงสัยว่าสองวิธีนี้ดีกว่า (อ่าน: pythonic เพิ่มเติม)

วิธีที่หนึ่ง : เพียงสร้างวัตถุจำลองและใช้สิ่งนั้น รหัสมีลักษณะดังนี้:

def test_one (self):
    mock = Mock()
    mock.method.return_value = True 
    self.sut.something(mock) # This should called mock.method and checks the result. 
    self.assertTrue(mock.method.called)

วิธีที่สอง : ใช้โปรแกรมแก้ไขเพื่อสร้างภาพจำลอง รหัสมีลักษณะดังนี้:

@patch("MyClass")
def test_two (self, mock):
    instance = mock.return_value
    instance.method.return_value = True
    self.sut.something(instance) # This should called mock.method and checks the result. 
    self.assertTrue(instance.method.called)

ทั้งสองวิธีทำสิ่งเดียวกัน ฉันไม่แน่ใจในความแตกต่าง

ใครช่วยสอนฉันได้บ้าง


10
ในฐานะคนที่ไม่เคยลอง Mock () หรือ patch มาก่อนฉันรู้สึกว่าเวอร์ชันแรกชัดเจนกว่าและแสดงให้เห็นว่าคุณต้องการทำอะไรแม้ว่าฉันจะไม่เข้าใจถึงความแตกต่างที่แท้จริงก็ตาม ฉันไม่รู้ว่านี่เป็นความช่วยเหลือใด ๆ หรือไม่ แต่ฉันคิดว่ามันอาจมีประโยชน์ในการถ่ายทอดสิ่งที่โปรแกรมเมอร์ที่ไม่ได้ฝึกหัดอาจรู้สึก
Michael Brennan

2
@MichaelBrennan: ขอบคุณสำหรับความคิดเห็นของคุณ มันมีประโยชน์แน่นอน
Sardathrion - ต่อต้านการละเมิด SE

คำตอบ:


151

mock.patchmock.Mockเป็นสัตว์มากแตกต่างกันมากกว่า patch แทนที่คลาสด้วยวัตถุจำลองและช่วยให้คุณทำงานกับอินสแตนซ์จำลองได้ ดูตัวอย่างนี้:

>>> class MyClass(object):
...   def __init__(self):
...     print 'Created MyClass@{0}'.format(id(self))
... 
>>> def create_instance():
...   return MyClass()
... 
>>> x = create_instance()
Created MyClass@4299548304
>>> 
>>> @mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
...   MyClass.return_value = 'foo'
...   return create_instance()
... 
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
...   print MyClass
...   return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass@4300234128
<__main__.MyClass object at 0x100505d90>

patchแทนที่MyClassด้วยวิธีที่ช่วยให้คุณควบคุมการใช้งานของคลาสในฟังก์ชันที่คุณเรียกใช้ เมื่อคุณแก้ไขคลาสแล้วการอ้างอิงถึงคลาสจะถูกแทนที่ด้วยอินสแตนซ์จำลองทั้งหมด

mock.patchโดยปกติจะใช้เมื่อคุณกำลังทดสอบบางสิ่งที่สร้างอินสแตนซ์ใหม่ของคลาสภายในการทดสอบ mock.Mockอินสแตนซ์มีความชัดเจนและเป็นที่ต้องการ หากself.sut.somethingวิธีการของคุณสร้างอินสแตนซ์MyClassแทนที่จะรับอินสแตนซ์เป็นพารามิเตอร์ก็mock.patchจะเหมาะสมที่นี่


2
@ D.Shawley เราจะแก้ไขกับคลาสที่สร้างอินสแตนซ์ภายในคลาสอื่นซึ่งต้องอยู่ระหว่างการทดสอบได้อย่างไร
ravi404

4
@ravz - ให้"แพทช์ที่จะ"อ่าน นี่เป็นหนึ่งในสิ่งที่ยากกว่าในการทำงานอย่างถูกต้อง
D.Shawley

ทดสอบการเยาะเย้ยฉันคือคล้ายกับวิธีที่สอง ฉันต้องการให้อินสแตนซ์ MyClass เพิ่มข้อยกเว้น ฉันได้ลองทั้ง mock.side_effect และ mock.return_value.side_effect แล้ว แต่ก็ไม่ได้ผล ฉันจะทำอย่างไร?
Hussain

5
@ D.Shawley ลิงก์เสียสามารถพบได้ที่นี่: "Where to Patch"
RazerM

2
ในการแก้ไขคลาสอ็อบเจ็กต์โปรดดูstackoverflow.com/questions/8469680/…
storm_m2138

27

ฉันมีวิดีโอ YouTubeเกี่ยวกับเรื่องนี้

คำตอบสั้น ๆ : ใช้mockเมื่อคุณผ่านสิ่งที่คุณต้องการล้อเลียนและpatchถ้าคุณไม่ได้ทำ ในสองอย่างนั้นการเยาะเย้ยเป็นสิ่งที่ต้องการอย่างยิ่งเพราะมันหมายความว่าคุณกำลังเขียนโค้ดด้วยการฉีดพึ่งพาที่เหมาะสม

ตัวอย่างโง่ ๆ :

# Use a mock to test this.
my_custom_tweeter(twitter_api, sentence):
    sentence.replace('cks','x')   # We're cool and hip.
    twitter_api.send(sentence)

# Use a patch to mock out twitter_api. You have to patch the Twitter() module/class 
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_tweeter(sentence):
    twitter_api = Twitter(user="XXX", password="YYY")
    sentence.replace('cks','x') 
    twitter_api.send(sentence)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.