Mock กับ MagicMock


144

ความเข้าใจของฉันคือMagicMockเป็นส่วนเหนือของMockที่ทำ "วิธีการมายากล" โดยอัตโนมัติดังนั้นจึงให้การสนับสนุนรายการการทำซ้ำและอื่น ๆ ได้อย่างราบรื่น ... แล้วอะไรคือเหตุผลที่ทำให้Mockธรรมดามีอยู่? นั่นไม่ใช่แค่MagicMockเวอร์ชันที่ถอดออกมาซึ่งสามารถละเลยได้จริงหรือ? ไม่จำลองชั้นรู้เทคนิคใด ๆ ที่ไม่สามารถใช้ได้ในMagicMock ?

คำตอบ:


102

อะไรคือเหตุผลที่ทำให้Mockธรรมดามีอยู่?

Michael Foord ผู้เขียน Mock ได้ตอบคำถามที่คล้ายกันมากที่ Pycon 2011 (31:00) :

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

ตอบ:คำตอบที่สมเหตุสมผลอย่างหนึ่งก็คือวิธีการทำงานของ MagicMock คือการกำหนดค่าวิธีการโปรโตคอลเหล่านี้ไว้ล่วงหน้าโดยการสร้าง Mocks ใหม่และตั้งค่าดังนั้นหากการจำลองใหม่ทุกครั้งสร้างการล้อเลียนใหม่จำนวนมากและตั้งค่าเหล่านั้นเป็นวิธีการโปรโตคอลและจากนั้นโปรโตคอลทั้งหมดเหล่านั้น วิธีการสร้างการล้อเลียนมากขึ้นและตั้งค่าไว้ในวิธีโปรโตคอลของพวกเขาคุณมีการเรียกซ้ำไม่สิ้นสุด ...

จะเกิดอะไรขึ้นถ้าคุณต้องการให้การเข้าถึงการจำลองของคุณเป็นวัตถุคอนเทนเนอร์เป็นข้อผิดพลาดคุณไม่ต้องการให้มันทำงาน? หากการเยาะเย้ยทุกครั้งมีวิธีโปรโตคอลทุกอย่างโดยอัตโนมัติการทำเช่นนั้นจะยากขึ้นมาก และนอกจากนี้ MagicMock จะทำการกำหนดค่าล่วงหน้าบางอย่างให้คุณโดยตั้งค่าการส่งคืนที่อาจไม่เหมาะสมดังนั้นฉันคิดว่ามันจะดีกว่าถ้ามีอุปกรณ์อำนวยความสะดวกนี้ที่มีทุกอย่างที่กำหนดไว้ล่วงหน้าและพร้อมใช้งานสำหรับคุณ แต่คุณสามารถจำลองแบบธรรมดาได้ด้วย วัตถุและเพียงกำหนดค่าวิธีการวิเศษที่คุณต้องการให้มีอยู่ ...

คำตอบง่ายๆคือใช้ MagicMock ได้ทุกที่หากเป็นพฤติกรรมที่คุณต้องการ


14
ฉันคิดว่าคำตอบที่ดีกว่าคือ: ใช้ MagicMock ถ้าคุณรู้ว่าคุณกำลังทำอะไรอยู่หรือใช้ Mock
laike9m

@ laike9m ฉันอ่านคำแนะนำในทางกลับกัน: เพียงใช้ MagicMock เว้นแต่คุณจะรู้ว่าคุณกำลังทำอะไรอยู่และต้องการพฤติกรรมที่เฉพาะเจาะจงซึ่งในกรณีนี้ให้เริ่มต้นด้วย Mock และหมุนของคุณเอง
Robino

@Robino ฉันไม่คิดอย่างนั้น ความคิดเห็นภายใต้คำตอบของ Sean Redmond อธิบายได้ดี
laike9m

เอกสารอย่างเป็นทางการแนะนำเป็นอย่างอื่น "เนื่องจาก MagicMock เป็นคลาสที่มีความสามารถมากกว่าจึงทำให้เป็นคลาสที่เหมาะสมที่จะใช้โดยค่าเริ่มต้น" docs.python.org/dev/library/…
Robino

58

ด้วย Mock คุณสามารถเยาะเย้ยวิธีการใช้เวทมนตร์ได้ แต่คุณต้องกำหนดมัน MagicMock มี"การเริ่มต้นใช้งานวิธีมายากลส่วนใหญ่" .

หากคุณไม่จำเป็นต้องทดสอบวิธีการใช้เวทมนตร์ใด ๆ การจำลองก็เพียงพอและไม่ได้นำสิ่งที่ไม่เกี่ยวข้องเข้ามาในการทดสอบของคุณ หากคุณต้องการทดสอบวิธีเวทย์มนตร์มากมาย MagicMock จะช่วยคุณประหยัดเวลาได้


แน่นอนว่าฉันได้อ่านเอกสารแล้ว นั่นไม่ได้ตอบคำถามของฉัน - ทำไมต้องกังวลกับ Mock ธรรมดาถ้า MagicMock ทำแบบเดียวกันและอื่น ๆ อีกมากมาย ฉันไม่เห็นสิ่งที่ไม่เกี่ยวข้องใด ๆ ในการทดสอบของฉัน - เพียงแค่ใช้ชื่ออื่นแค่นั้นแหละ แล้วการจับอยู่ที่ไหน?
Vladimir Ignatov

39
การทดสอบควรมีน้อยที่สุดและวัตถุจำลองควรใช้งานได้น้อยที่สุดเพื่อให้คุณแน่ใจว่าคุณกำลังทดสอบอะไรอยู่ หากคุณใช้ MagicMock เพียงเพราะทำได้มากกว่า แต่คุณไม่ได้ทดสอบอย่างชัดเจนว่า "มากกว่า" ทั้งหมดคุณจะเสี่ยงต่อการทดสอบล้มเหลวเนื่องจากพฤติกรรมของ MagicMock เริ่มต้น ความล้มเหลวนี้อาจสะท้อนถึงบางสิ่งเกี่ยวกับค่าเริ่มต้นของ MagicMock มากกว่าสิ่งที่ควรเยาะเย้ย ที่แย่กว่านั้นคือคุณเสี่ยงต่อการที่การทดสอบจะประสบความสำเร็จในเวลาที่ควรจะล้มเหลว ความเสี่ยงมีน้อย แต่ถ้าเกิดขึ้นจะเสียเวลาของคุณมาก
Sean Redmond

1
ฉันคิดว่ามันเหมือนกับการใช้ JS เทียบกับ Jquery ธรรมดา แน่นอนว่าคุณสามารถใช้ Jquery เพื่อทำ JS ทั้งหมดของคุณได้ แต่ในบางกรณีคุณแค่ต้องการใช้เครื่องมือขั้นต่ำที่จำเป็นในการทำงานให้เสร็จ ฉันพบว่ากรณีเหล่านี้มักจะง่ายมากหรือซับซ้อนมาก
แอบอิง

54

เริ่มต้นด้วยMagicMockคลาสย่อยของMock.

class MagicMock(MagicMixin, Mock)

ด้วยเหตุนี้ MagicMock จึงมอบทุกสิ่งที่ Mock มอบให้และอีกมากมาย แทนที่จะคิดว่า Mock เป็นเวอร์ชันที่ถูกถอดออกของ MagicMock ให้นึกถึง MagicMock เป็นเวอร์ชันขยายของ Mock สิ่งนี้ควรตอบคำถามของคุณเกี่ยวกับสาเหตุที่ Mock มีอยู่และ Mock ให้อะไรกับ MagicMock

ประการที่สอง MagicMock นำเสนอการใช้งานตามค่าเริ่มต้นของวิธีการมายากลจำนวนมาก / ส่วนใหญ่ในขณะที่ Mock ไม่ทำ ดูที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการมายากลที่มีให้

ตัวอย่างบางส่วนของวิธีการมายากลที่มีให้:

>>> int(Mock())
TypeError: int() argument must be a string or a number, not 'Mock'
>>> int(MagicMock())
1
>>> len(Mock())
TypeError: object of type 'Mock' has no len()
>>> len(MagicMock())
0

และสิ่งเหล่านี้อาจไม่ใช้งานง่าย (อย่างน้อยก็ไม่ง่ายสำหรับฉัน):

>>> with MagicMock():
...     print 'hello world'
...
hello world
>>> MagicMock()[1]
<MagicMock name='mock.__getitem__()' id='4385349968'>

คุณสามารถ "ดู" วิธีการที่เพิ่มลงใน MagicMock เมื่อมีการเรียกใช้วิธีการเหล่านั้นเป็นครั้งแรก:

>>> magic1 = MagicMock()
>>> dir(magic1)
['assert_any_call', 'assert_called_once_with', ...]
>>> int(magic1)
1
>>> dir(magic1)
['__int__', 'assert_any_call', 'assert_called_once_with', ...]
>>> len(magic1)
0
>>> dir(magic1)
['__int__', '__len__', 'assert_any_call', 'assert_called_once_with', ...]

ทำไมไม่ใช้ MagicMock ตลอดเวลา?

คำถามกลับมาหาคุณคือคุณโอเคกับการใช้เมธอดเริ่มต้นหรือไม่? เช่นmocked_object[1]จะไม่ผิดพลาดหรือไม่? คุณโอเคกับผลลัพธ์ที่ไม่ได้ตั้งใจอันเนื่องมาจากการใช้เมธอดมายากลที่มีอยู่แล้วหรือไม่?

หากคำตอบสำหรับคำถามเหล่านี้คือใช่ให้ใช้ MagicMock ไม่งั้นติด Mock


13

นี่คือสิ่งที่เอกสารอย่างเป็นทางการของ python กล่าวว่า:

ในตัวอย่างส่วนใหญ่คลาส Mock และ MagicMock สามารถใช้แทนกันได้ เนื่องจาก MagicMock เป็นคลาสที่มีความสามารถมากกว่าจึงทำให้ใช้งานได้ตามค่าเริ่มต้น


3

ฉันพบอีกกรณีหนึ่งที่ความเรียบง่าย Mockอาจมีประโยชน์มากกว่าMagicMock:

In [1]: from unittest.mock import Mock, MagicMock, ANY
In [2]: mock = Mock()
In [3]: magic = MagicMock()
In [4]: mock.foo == ANY
Out[4]: True
In [5]: magic.foo == ANY
Out[5]: False

การเปรียบเทียบANYจะมีประโยชน์ตัวอย่างเช่นการเปรียบเทียบเกือบทุกคีย์ระหว่างพจนานุกรมสองพจนานุกรมที่มีการคำนวณค่าบางค่าโดยใช้การจำลอง

สิ่งนี้จะใช้ได้หากคุณใช้Mock:


self.assertDictEqual(my_dict, {
  'hello': 'world',
  'another': ANY
})

ในขณะที่มันจะเพิ่มขึ้นAssertionErrorถ้าคุณใช้MagicMock

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.