Monkey Patch란?

업무를 하면서 가끔 회사에서 Monkey Patch를 하고...라는 말을 듣는다.

무슨뜻이지? 하면서 한번도 찾아본적이 없어서 이번에 찾아보고 정리한다.

 

위키피디아에 이렇게 정리되어 있다.

 

A monkey patch is a way for a program to extend or modify supporting system software locally (affecting only the running instance of the program).

해석을 한다면 monkey patch는 프로그램이 내부 프로그램을 로컬에서 잠시 다른 기능을 수행하도록 수정하거나 확장하도록 하는 방법이며 이는 로컬이므로 런타임시에만 영향을 주는 방식이다.

 

예시로는 Python으로 아래와 같이 있다.

>>> import math
>>> math.pi
3.141592653589793
>>> math.pi = 3
>>> math.pi
3
>>> ================================ RESTART ================================
>>> import math
>>> math.pi
3.141592653589793
>>>

위의 내용만 봐도 이해는 했을 것이다.

그런데 어디다가 쓰지?

 

예상은 했겠지만, Test할때 주로 쓴다.

그리고 다른 기능을 수행하도록 하는 부분 또한 Mock Object를 원하는 기능이 수행하도록 만든 뒤 그것으로 replace하는 것이다.

 

my_calendar.py

import requests
from datetime import datetime

def is_weekday():
    today = datetime.today()
    return (0 <= today.weekday() < 5)

def get_holidays():
    r = requests.get('http://xxx.holiday.org/api/holidays')
    if r.status_code == 200:
        return r.json()
    return None

위와 같은 코드를 만들었고, 이제 테스트를 해야 한다.

get_holidays() 함수는 requests가 필요한데 아직 위의 url에서는 해당 api를 서비스하지 않고 있다.

즉, 테스트를 하기가 어렵다.

 

아래와 같이 tests.py를 한번 보자.

 

import unittest
from my_calendar import get_holidays
from requests.exceptions import Timeout
from unittest.mock import patch

class TestCalendar(unittest.TestCase):
    @patch('my_calendar.requests')
    def test_get_holidays_timeout(self, mock_requests):
            mock_requests.get.side_effect = Timeout
            with self.assertRaises(Timeout):
                get_holidays()
                mock_requests.get.assert_called_once()

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

위의 코드는 my_calenadar class를 Test하기 위한 코드이다.

 

@patch('my_calendar.requests') 이부분이 위에 설명되었던 Monkey Patch부분이다.

@patch decorator에 들어가는 파라메터(my_calendar.requests)는 replace 대상(target)이 되는 속성이다.

즉, my_calendar 소스내에 import requests 가 있는데 이걸 내가 원하는 requests로 바꿀려고 한다.

그럼 바꿀려고 하는 mock object 참조변수는 def test_get_holidays_timeout(self, mock_requests) 의 mock_requests이다.

 

따라서 해당 함수 안에서 mock_requests.get.side_effect = Timeout 즉 해당 request의 get() 함수는 실제 url에 접속하고 관계없이,

Timeout을 Raise해줘.. 라는 것으로 가짜 object를 만들었다. 이제 @patch decorator를 통해 실제 my_calendar.py의 requests 객체를 바꾸었으니 Exception Test가 가능해졌다.

 

with 문 이하는 본 monkey patch와 관계없는 Mock관련 사항이므로 설명을 생략한다.

 

따로 Python의 Mock 관련해서는 설명 글을 게시할 예정이다.

 

정리하자면 위와 같이 requests 는 아시겠지만 이를 테스트할려면 해당 api서버가 개발을 해주거나 서비스를 제공해야 테스트 가능하다.

이부분을 내가 로컬에서 단위테스트를 하기 위해서는 원래의 requests를 내가 원하는 방식으로 바꿔줘야 하는데 이게 monkey patching이며, 원하는 방식으로 바꾸기 위한 임시객체(가짜객체)를 Mock Object라 하며 Python에서는 unittest.mock에서 강력한 기능들을 제공한다.

 

끝.

'Python' 카테고리의 다른 글

Python Celery Task Monitoring  (0) 2019.10.08
Python Lambda  (0) 2019.09.03
Celery from scratch  (0) 2019.05.20
Python Excel to MySQL  (0) 2019.04.12
Database 정보 CSV 작성방법  (0) 2019.04.10

+ Recent posts