# 使用pytest-mock時如何控制mock object何時結束
###### tags: `unit-test` `pytest`
在使用pytest-mock的mocker fixture時
所有mock假造的物件都會在當個測試結束時被關掉,所以不會影響其他測試
但如果發生了以下的事情
```python=
def test_function(mocker):
mock1 = mocker.patch('module1.function1')
# Do some test using mock1 to generate something you want
# Stop mock1 from being in effect
mocker.stop(mock1)
# Do some other test without mock1
```
在很不單元的測試內(比如撰寫legacy code的測試),這種狀況常會發生
在同一個測試function內,有時候需要同一個function有時是被mock的,有時是維持正常狀況運作的
這時候stop就會派上用場
只要在測試內使用mocker.stop(),就可以在想要的時機點把對應的MagicMock物件關掉
會讓複雜的測試好寫不少
## 與unittest.mock的不同
由於文件的缺乏,pytest-mock的doc會叫你去看unittest.mock的文件
```python=
patcher = patch('package.module.ClassName')
from package import module
original = module.ClassName
new_mock = patcher.start()
assert module.ClassName is not original
assert module.ClassName is new_mock
patcher.stop()
assert module.ClassName is original
assert module.ClassName is not new_mock
```
但是使用mocker.stop時不能直接把patch完後的MagicMock物件照著上面的作法stop
mocker的source code是這樣
```python=
def stop(self, mock: unittest.mock.MagicMock) -> None:
"""
Stops a previous patch or spy call by passing the ``MagicMock`` object
returned by it.
"""
for index, (p, m) in enumerate(self._patches_and_mocks):
if mock is m:
p.stop()
del self._patches_and_mocks[index]
break
else:
raise ValueError("This mock object is not registered")
```
所以使用上就會像上個小節一樣,把patch完或spy完的MagicMock物件丟進去就可以了
```python=
mock1 = mocker.patch('module1.function1')
mocker.stop(mock1)
```
## stopall
mocker.stopall()可以把所有還在運作的mock都砍掉,也可以達成類似的效果
## 結語
~~文件真是個好東西呢~~
本篇隨筆可以讓各位理解,如果在單個測試過程中想要把某個或全部的mock都關掉時該怎麼做
而當然,各位可能會覺得:測試是不是不該這樣寫
畢竟,mock的東西開開關關,會讓測試本身就難以理解,也更有可能在不預期的地方出錯
但在某些狀況下(如前文所提,處理legacy code的測試),mocker.stop()會降低大型的測試撰寫的難度,而在大型測試存在的狀況下,我們才有辦法開始修改測試與主程式。
那麼以上就是有關pytest-mock stop的用法,希望能幫助到正在撰寫測試的各位。