# mock, patch 與 namespace ###### tags: `unit-test` ## 引言 如果你曾經用過unittest.mock.patch,不知道在使用時你有沒有一個疑問: 我這個patch用下去,究竟要patch哪裡? ~~我是誰,我在哪裡~~ ## 案例 如果今天我們的資料夾結構長這樣 ``` root/ ----app/ ----connection.py ----test/ ----test_connection.py ``` 在connection.py內,是一個簡單的request ```python= # connection.py in ./app import requests def connection(): # try to call a website # default it return 200 response = requests.get('https://www.google.com.tw/') return response.status_code ``` 然後今天在測試的時候,如果是使用mock.patch不久的人,應該會有個顯著的疑惑 ```python= # test_connection.py from app import connection def test_connection(mocker): # create mock object mock_response = mocker.Mock() mock_response.status_code = 200 # patch requests mock_requests = mocker.patch("app.connection.requests") mock_requests.get.return_value = mock_response # act response = connection.connection() assert response == 200 ``` 在寫到第11行的時候,到底patch要指到哪裡才是對的呢? 究竟是patch 1. "app.connection.requests" 2. "app.connection.requests.get" 3. "requests" 4. "requests.get" 究竟該patch誰才是對的? 如果在這個地方放棄思考,用try&error的方式硬幹當然是可以的,但沒有搞懂這裡的邏輯的話不說可惜,之後照寫的測試可能會怪怪的(汗 ## Namespace 在揭曉答案之前,有個基本的東西需要知道 在各位import module的時候,有沒有想過import到底做了什麼呢? 例如一個簡單的範例,我們在A.py裡import ```python= import requests ``` 這時python會把requests的名子抓過來,放到這個A.py的namespace裡 這時候如果在這個A.py內呼叫 ```python= dir() ``` 就會看到 ``` ['__builtins__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'requests'] ``` requests現在就會被A.py看到了 ## unittest.mock.patch patch究竟做了什麼事情呢? 實際上的運作是,patch會把namespace內的某個指到的module換成的MagicMock object,所以要讓patch正確做事,我們必須 1. 確保patch的東西是被測試的file(connection.py)看得到的人 2. 確認在測試file(test_connection.py)中對應的module可以被import 我們以前列的connection.py為例子 ```python= # connection.py in ./app import requests def connection(): # try to call a website # default it return 200 response = requests.get('https://www.google.com.tw/') return response.status_code ``` 這時候如果在這裡dir(),如同上個章節所言,我們會得到 ``` [..., 'requests'] ``` 所以,如果在測試中要patch掉這裡的requests,以patch的定義來說,我們必須 **patch("app.connection.requests")** 最後,不管用得是patch還是patch.object,他們的souce code其實最後都一樣,所以如果用的是patch.object的注意事項也一樣 ```python= return _patch( getter, attribute, new, spec, create, spec_set, autospec, new_callable, kwargs, unsafe=unsafe ) ``` > 有興趣的可以去這裡 https://github.com/python/cpython/blob/main/Lib/unittest/mock.py#L1317 https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch ## 後記 本篇是遠遠不到講完,甚至可能連講清楚都欠奉的一篇文章,所以理所當然的有下集(~~其實是寫不完~~) 下一篇文繼續深入講解這個主題 令人混淆的mock.patch,他還是高人氣的秘密在哪裡呢? 主要會講講where to patch跟一些案例