## 引言
續上篇講完使用patch的正確方式後,這篇文我們會再深入一點探討這個問題。
## Where to patch?
在unittest的官方文件中,有這麼一段話:
**The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.**
這句話是什麼意思呢? 我們用範例來講解,現在我們的目標是在測試module_b.py時mock住my_function:
```python=
# module_a.py
def my_function():
return 10
# module_b.py
from module_a import my_function
def use_my_function():
result = my_function()
return result + 5
```
在這個範例中,use_my_function 函數引用了 my_function,但是 my_function 是從 module_a 中匯入的。
那麼對module_b來說,現在他的namespace內有誰呢? 如果還對前一篇文章有印象的話,這時候namespace內會有"my_function"
```python=
dir() # [..., 'my_function']
```
因此,如果我們使用:
```python=
patch('module_a.my_function')
```
會沒有作用,因為module_b中在import時已經將my_function拿到自己的namespace內了,這時候mock源頭(**where it is defined**)的function並不會成功的mock掉module_b中的物件。
正確的mock手段為,在module_b真正去找這個function的位置(**where an object is looked up**)使用mock,如下:
```python=
patch('module_b.my_function')
```
用同樣的概念,我們考慮另一種情況:
```python=
# module_b.py
import module_a
def use_my_function():
result = module_a.my_function()
return result + 5
```
在這個狀況中,module_b的namespace中有的是module_a而非my_function,因此此時look up的位置即為module_a
```python=
patch('module_a.my_function')
```
從以上的講解中,不難發現patch是個相當容易搞混的動作,但通常只要觀察patch被測試者最終看到的namespace,就能避免大部分的問題。
## import 運行順序
在講到mock的問題時,除了上述的定則,有時我們還得注意程式執行的順序,例如:
* 在測試運行到欲patch的物件時,該物件究竟是不是已經被執行過,例如singleton物件或是module的attribute
* 例如執行flask測試時,可能有某些library已經在單元測試發生之前就已經宣告
* .....
這部份的問題會更為細項,通常在想mock的物件越接近library時會更容易發生,但如果遵守前個章節的內容,大致上都不會出錯。
## 結語
unittest.mock.patch是個非常非常強大的工具,基本上他真的可以"暫時"取代掉任何物件,所以在使用上就必須要很小心,同時釐清一些守則與流程,才能避免測試的表現與想像中不一致的問題。
以上,如果內容有疑問或有可更正的部分,歡迎各位留言討論。
那麼,祝各位的專案測試都能有coverage過九成的一天。