Python Mocking ฟังก์ชันจากโมดูลที่นำเข้า


125

ฉันต้องการเข้าใจวิธี@patchการทำงานจากโมดูลที่นำเข้า

นี่คือที่ที่ฉันมาไกลมาก

app / mocking.py:

from app.my_module import get_user_name

def test_method():
  return get_user_name()

if __name__ == "__main__":
  print "Starting Program..."
  test_method()

app / my_module / __ init__.py:

def get_user_name():
  return "Unmocked User"

การทดสอบ / mock-test.py:

import unittest
from app.mocking import test_method 

def mock_get_user():
  return "Mocked This Silly"

@patch('app.my_module.get_user_name')
class MockingTestTestCase(unittest.TestCase):

  def test_mock_stubs(self, mock_method):
    mock_method.return_value = 'Mocked This Silly')
    ret = test_method()
    self.assertEqual(ret, 'Mocked This Silly')

if __name__ == '__main__':
  unittest.main()

สิ่งนี้ไม่ได้ผลตามที่ฉันคาดหวัง ว่า "ปะ" โมดูลเพียงแค่ส่งกลับค่า unmocked get_user_nameของ ฉันจะจำลองวิธีการจากแพ็คเกจอื่น ๆ ที่ฉันนำเข้ามาในเนมสเปซที่อยู่ระหว่างการทดสอบได้อย่างไร


1
คำถามเกี่ยวกับ "การล้อเลียนแนวทางปฏิบัติที่ดีที่สุด" หรือว่าสิ่งที่คุณกำลังทำนั้นสมเหตุสมผลหรือไม่? เกี่ยวกับครั้งแรกผมว่าจะใช้เยาะเย้ยห้องสมุดเช่นMockซึ่งจะรวมอยู่ใน python3.3 + unittest.mockเป็น
Bakuriu

ฉันกำลังถามว่าฉันจะทำเรื่องนี้ถูกไหม ฉันมองไปที่ Mock แต่ฉันไม่เห็นวิธีแก้ปัญหานี้โดยเฉพาะ มีวิธีสร้างสิ่งที่ฉันทำข้างต้นใน Mock ขึ้นมาใหม่หรือไม่?
nsfyn55

คำตอบ:


167

เมื่อคุณกำลังใช้patchมัณฑนากรจากunittest.mockแพคเกจที่คุณไม่ได้ปะ namespace โมดูลนำเข้าจาก (ในกรณีนี้app.my_module.get_user_name) คุณกำลัง patching ใน namespace app.mocking.get_user_nameภายใต้การทดสอบ

ในการดำเนินการข้างต้นโดยMockลองทำสิ่งต่างๆดังต่อไปนี้:

from mock import patch
from app.mocking import test_method 

class MockingTestTestCase(unittest.TestCase):

    @patch('app.mocking.get_user_name')
    def test_mock_stubs(self, test_patch):
        test_patch.return_value = 'Mocked This Silly'
        ret = test_method()
        self.assertEqual(ret, 'Mocked This Silly')

เอกสารคู่มือไลบรารีมาตรฐานประกอบด้วยส่วนที่เป็นประโยชน์ซึ่งอธิบายถึงสิ่งนี้


สิ่งนี้ทำให้เกิดปัญหาของฉัน อยู่ในโมดูลที่แตกต่างกว่าget_user_name test_methodมีวิธีล้อเลียนบางสิ่งในโมดูลย่อยหรือไม่? ฉันแก้ไขด้วยวิธีที่น่าเกลียดด้านล่าง
nsfyn55

6
ไม่สำคัญว่าget_user_nameจะอยู่ในโมดูลอื่นนอกเหนือจากที่test_methodคุณกำลังนำเข้าฟังก์ชันลงในฟังก์ชันapp.mockingนั้นจะอยู่ในเนมสเปซเดียวกัน
Matti John

2
test_patch มาจากไหนมันคืออะไรกันแน่?
Mike G

2
test_patch ถูกส่งผ่านโดยผู้ตกแต่งโปรแกรมแก้ไขและเป็นอ็อบเจ็กต์ get_user_name ที่ถูกจำลอง (เช่นอินสแตนซ์ของคลาส MagicMock) get_user_name_patchมันอาจจะเป็นที่ชัดเจนว่ามันเป็นชื่อสิ่งที่ต้องการ
Matti John

คุณอ้างอิง test_method อย่างไร จะทำให้เกิดข้อผิดพลาด NameError: ไม่ได้กำหนดชื่อสากล 'test_method'
Aditya

12

แม้ว่าคำตอบของ Matti John จะช่วยแก้ปัญหาของคุณได้ (และช่วยฉันด้วยขอบคุณ!) แต่ฉันขอแนะนำให้แปลการแทนที่ฟังก์ชัน 'get_user_name' ดั้งเดิมด้วยฟังก์ชันที่ถูกเยาะเย้ย วิธีนี้จะช่วยให้คุณควบคุมได้ว่าจะเปลี่ยนฟังก์ชันเมื่อใดและเมื่อใดไม่ได้ นอกจากนี้สิ่งนี้จะช่วยให้คุณทำการเปลี่ยนได้หลายครั้งในการทดสอบเดียวกัน ในการดำเนินการดังกล่าวให้ใช้สถานะ 'ด้วย' ในลักษณะที่คล้ายคลึงกัน:

from mock import patch

class MockingTestTestCase(unittest.TestCase):

    def test_mock_stubs(self):
        with patch('app.mocking.get_user_name', return_value = 'Mocked This Silly'):
            ret = test_method()
            self.assertEqual(ret, 'Mocked This Silly')

6
นี่เป็นสิ่งที่ไม่สำคัญสำหรับคำถามที่วางไว้ ไม่ว่าคุณจะใช้patchเป็นมัณฑนากรหรือตัวจัดการบริบทจะเฉพาะสำหรับกรณีการใช้งาน ตัวอย่างเช่นคุณสามารถใช้patchเป็นมัณฑนากรเพื่อจำลองค่าสำหรับการทดสอบทั้งหมดในxunitหรือpytestคลาสในขณะที่ในกรณีอื่นมันมีประโยชน์ที่จะมีการควบคุมแบบละเอียดที่ผู้จัดการบริบทจัดหาให้
nsfyn55
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.