Mock Object ในภาษา Python
Mock Object คืออะไร
Mock Object คือวิธีการที่ใช้ในการกำหนดพฤติกรรม Object เพื่อให้ได้ผลลัพธ์ที่ใช้ในการทำ Unit Test ตาม Environment ที่ต้องการ โดยจะเป็นการสมมุติว่า ฟังก์ชันทำงานถูกต้องและจะทำการ Mock ข้อมูลขึ้นมาเพื่อใช้ทำการทดสอบ
สาเหตุที่ต้องใช้หลัก ๆ Python Mock Object เพื่อควบคุมสิ่งที่เกิดขึ้นในการทำ UnitTest
ในบางครั้ง ต้องทำการทดสอบฟังก์ชัน เช่น get_coin_price ซึ่งจะทำการแสดงข้อมูลราคาของเหรียญ โดยดึงจากฐานข้อมูล ซื่งถ้าใช้ฐานข้อมูลจริงในบางครั้งผลลัพธ์ที่ได้อาจจะไม่ถูกต้อง เพราะฐานข้อมูลอาจจะมีการเปลี่ยนแปลงซึ่งยากที่จะควบคุม ดังนั้น จึงเป็นสาเหตุที่ดีกว่าที่จะทำการทดสอบโค้ดในสถานการที่ควบคุมได้ โดยการใช้ Mock Object เพื่อทำการจะให้ฟังก์ชันได้ผลลัพธ์จะเป็นทั้ง successful or failures response
ภาษา Python นั้นได้มี Library สำหรับการทำ unittest mock โดยสามารถทำการติดตั้งผ่าน Pip package manager ได้เลย
$ pip install mock
การประกาศ Mock Object
from unittest.mock import Mock
mock = Mock()
print(mock)>> <Mock id='140519256300896'>
การประกาศ Mock Object สำหรับ Mock Function
from unittest.mock import Mock
mock = Mock()
print(mock.get_coin_price()) >> <Mock name='mock.get_coin_price()' id='140379447928048'>
assert_called_once เป็นการตรวจสอบว่า mock object นั้นได้ถูกเรียก 1 ครั้ง
โดยจากโค้ดทำการ Mock Object ให้ทำการเรียกใช้ a_plus_b() โดยจะทำการตรวจสอบว่าฟังก์ชันนี้ได้ทำการเรียกใช้แล้ว 1 ครั้งหรือไม่
from unittest.mock import Mock
def a_plus_b(a,b): return a+b
mock = Mock()
mock.a_plus_b()
mock.a_plus_b.assert_called_once()
หากทำการลบ mock.a_plus_b() ก็จะทำให้มันไม่ถูกเรียกใช้ และการทดสอบจะ fail
assert_called_with เป็นการตรวจสอบว่า Mock Object นั้นได้ถูกเรียกด้วยข้อมูลชุดนี้หรือไม่
โดยจากโค้ดทำการ Mock Object ขี้นมา mock.a_plus_b(a=20, b=25) โดยให้รับตัวแปร 2 ตัว จากนั้นบรรทัดถัดไปจึงมาทำการตรวจสอบว่าฟังก์ขั่นถูกเรียกด้วยข้อมูลที่เหมือนกันหรือไม่
from unittest.mock import Mock
def a_plus_b(a,b): return a+b
mock = Mock()
mock.a_plus_b(a=20, b=25)
mock.a_plus_b.assert_called_with(a=20, b=25)
return_value ใช้ในการตั้งค่าข้อมูลที่จะถูกส่งกลับมาจาก Mock Object ของฟังก์ชัน โดยจากโค้ดจะทำการตั้งค่าให้ข้อมูลที่ถูกส่งกลับมาคือ 30
from unittest.mock import Mock
def a_plus_b(a,b): return a+b
value = 30
mock = Mock()# ทำการ set ค่าให้ส่ง 30 กลับมาเมื่อเรียกใช้
mock_value = mock.a_plus_b().return_value = value
assert mock_value == value
print(mock.a_plus_b().return_value)>> 30
side_effect เป็นการ Mock Object ให้ฟังก์ชันที่โดน Mock นั้นเกิดผลลัพธ์ได้หลายแบบ เช่นจากโด้ดจะตั้งค่าให้หากเรียกใช้ครั้งแรกให้ใช้ค่าเลข 3 หากถูกเรียกครั้งถัดมาจะเป็น 2 ตามลำดับ และในฟังก์ชันสามารถรับค่าเป็น list หรือ ฟังก์ชันได้
from unittest.mock import Mock
mock = Mock()
mock.side_effect = [3, 2, 1]
print(mock(), mock(), mock())>> 3 2 1
call_count เป็นฟังก์ชันที่ใช้ในการนับจำนวนครั้งที่ฟังก์ชันที่ทำการ Mock Object ถูกเรียกใช้งาน
mock = Mock(return_value=None)
print(mock.call_count)>> 0
mock()
mock()
mock.call_count
print(mock.call_count)>> 2
ตัวอย่างการใช้ mock object ใน unittest
ถัดไปจะเป็นตัวอย่างการ Mock Object ทั้ง Request และ Response เนื่องจากต้องการทดสอบว่าหากทำการเรียกใช้ API ไป 2 ครั้ง โดยครั้งแรกจะควบคุมให้เมื่อเรียกใช้ API ให้บังคับให้เกิดเหตุการณ์ request timeout ส่วนครั้งที่ 2 สามารถเรียกใช้ ได้ปกติและได้ข้อมูลกลับมา
import unittest
from unittest.mock import Mock
from requests.exceptions import Timeout# Mock request
requests = Mock()
def get_bitcoin_price(): r = requests.get('http://localhost/api/crypto') if r.status_code == 200: return r.json() return None
class TestCrypto(unittest.TestCase): def test_crypto(self): # Mock response response_mock = Mock() response_mock.status_code = 200 response_mock.json.return_value = { 'name': 'Bit Coin', 'price': 40000, 'rank': 1, } # Set side_effect สำหรับ .get method requests.get.side_effect = [Timeout, response_mock] # ทดสอบให้การเรียกใช้ครั้งแรกติด Timeout with self.assertRaises(Timeout): get_bitcoin_price() # ทดสอบครั้งที่สองสำเร็จให้ตรวจสอบข้อมูล price มีค่าเท่ากีบ 40000 assert get_bitcoin_price()['price'] == 40000 # ทำการทดสอบ .get method ถูกเรียกใช้ไป 2 ครั้ง assert requests.get.call_count == 2
if __name__ == '__main__': unittest.main()
แหล่งอ้างอิง
• Unittest.mock — Mock object library, Python Development Tool.
• Understanding the Python Mock Object Library, by Alex Ronquillo.
![Senna Labs](/_next/image?url=%2Fsenalabs.jpg&w=3840&q=75)
![](/_next/image?url=%2Fimages%2Fsubscribe.webp&w=3840&q=75)
Subscribe to follow product news, latest in technology, solutions, and updates
Other articles for you
Let’s build digital products that are simply awesome !
We will get back to you within 24 hours!Go to contact us![](/_next/image?url=%2Fimages%2Ftell-us-arrow.webp&w=384&q=75)
![Contact ball](/_next/image?url=%2Fimages%2Fcontact-ball.webp&w=3840&q=75)
![Contact us bg 2](/_next/image?url=%2Fimages%2Fcontact-us-bg-2.webp&w=3840&q=75)
![Contact us bg 4](/_next/image?url=%2Fimages%2Fcontact-us-bg-4.webp&w=3840&q=75)
![Contact us bg 1](/_next/image?url=%2Fimages%2Fcontact-us-bg-1.webp&w=3840&q=75)
![Ball left](/_next/image?url=%2Fimages%2Fball-left.png&w=1080&q=75)
![Ball right](/_next/image?url=%2Fimages%2Fball-right.png&w=1920&q=75)
![Ball left](/_next/image?url=%2Fimages%2Fball-left.png&w=256&q=75)
![Ball right](/_next/image?url=%2Fimages%2Fball-right.png&w=384&q=75)
![Sennalabs gray logo](/_next/image?url=%2Fimages%2Fsennalabs-gray-logo.webp&w=256&q=75)